@@ -74,7 +74,10 @@ export class Fork {
7474 throw Error ( 'Fork#marshal node.selfAddress is not set' )
7575 }
7676 const data : Uint8Array [ ] = [ ]
77- data . push ( new Uint8Array ( [ this . node . determineType ( ) ] ) )
77+ // Re-emit the type byte read from the chunk when the node is untouched, so an
78+ // unmarshal → marshal round-trip is byte-identical. determineType() can only
79+ // recompute it correctly once the node's children are loaded/built in memory.
80+ data . push ( new Uint8Array ( [ this . node . type ?? this . node . determineType ( ) ] ) )
7881 data . push ( Binary . numberToUint8 ( this . prefix . length ) )
7982 data . push ( this . prefix )
8083
@@ -126,7 +129,7 @@ export class Fork {
126129 metadata = JSON . parse ( DECODER . decode ( reader . read ( metadataLength ) ) )
127130 }
128131
129- return new Fork ( prefix , new MantarayNode ( { selfAddress, metadata, path : prefix } ) )
132+ return new Fork ( prefix , new MantarayNode ( { selfAddress, metadata, path : prefix , type } ) )
130133 }
131134}
132135
@@ -137,6 +140,7 @@ interface MantarayNodeOptions {
137140 metadata ?: Record < string , string > | null
138141 path ?: Uint8Array | null
139142 parent ?: MantarayNode | null
143+ type ?: number | null
140144}
141145
142146export class MantarayNode {
@@ -147,6 +151,7 @@ export class MantarayNode {
147151 public path : Uint8Array = new Uint8Array ( 0 )
148152 public forks : Map < number , Fork > = new Map ( )
149153 public parent : MantarayNode | null = null
154+ public type : number | null = null
150155
151156 constructor ( options ?: MantarayNodeOptions ) {
152157 if ( options ?. targetAddress ) {
@@ -172,6 +177,8 @@ export class MantarayNode {
172177 if ( options ?. parent ) {
173178 this . parent = options . parent
174179 }
180+
181+ this . type = options ?. type ?? null
175182 }
176183
177184 get fullPath ( ) : Uint8Array {
@@ -243,14 +250,23 @@ export class MantarayNode {
243250 fork . node . selfAddress = ( await fork . node . calculateSelfAddress ( ) ) . toUint8Array ( )
244251 }
245252 }
253+ const hasEntry = ! Binary . equals ( this . targetAddress , NULL_ADDRESS )
254+ let refBytesSize = 0
255+
256+ if ( hasEntry ) {
257+ refBytesSize = this . targetAddress . length
258+ } else {
259+ for ( const fork of this . forks . values ( ) ) {
260+ if ( fork . node . selfAddress ) {
261+ refBytesSize = fork . node . selfAddress . length
262+ break
263+ }
264+ }
265+ }
246266 const header = new Uint8Array ( 32 )
247267 header . set ( VERSION_02_HASH , 0 )
248- header . set (
249- Binary . equals ( this . targetAddress , NULL_ADDRESS ) && Binary . equals ( this . path , new Uint8Array ( [ 47 ] ) )
250- ? Binary . numberToUint8 ( 0 )
251- : Binary . numberToUint8 ( this . targetAddress . length ) ,
252- 31 ,
253- )
268+ header . set ( Binary . numberToUint8 ( refBytesSize ) , 31 )
269+ const entry = hasEntry ? this . targetAddress : new Uint8Array ( refBytesSize )
254270 const forkBitmap = new Uint8Array ( 32 )
255271 for ( const fork of this . forks . keys ( ) ) {
256272 Binary . setBit ( forkBitmap , fork , 1 , 'LE' )
@@ -261,17 +277,7 @@ export class MantarayNode {
261277 forks . push ( this . forks . get ( i ) ! . marshal ( ) )
262278 }
263279 }
264- const data = Binary . xorCypher (
265- Binary . concatBytes (
266- header ,
267- Binary . equals ( this . targetAddress , NULL_ADDRESS ) && Binary . equals ( this . path , new Uint8Array ( [ 47 ] ) )
268- ? new Uint8Array ( 0 )
269- : this . targetAddress ,
270- forkBitmap ,
271- ...forks ,
272- ) ,
273- this . obfuscationKey ,
274- )
280+ const data = Binary . xorCypher ( Binary . concatBytes ( header , entry , forkBitmap , ...forks ) , this . obfuscationKey )
275281
276282 return Binary . concatBytes ( this . obfuscationKey , data )
277283 }
@@ -311,11 +317,14 @@ export class MantarayNode {
311317 const targetAddress = targetAddressLength ? reader . read ( targetAddressLength ) : NULL_ADDRESS
312318 const node = new MantarayNode ( { selfAddress, targetAddress, obfuscationKey } )
313319 const forkBitmap = reader . read ( 32 )
314- for ( let i = 0 ; i < 256 ; i ++ ) {
315- if ( Binary . getBit ( forkBitmap , i , 'LE' ) ) {
316- const newFork = Fork . unmarshal ( reader , selfAddress . length )
317- node . forks . set ( i , newFork )
318- newFork . node . parent = node
320+
321+ if ( targetAddressLength > 0 ) {
322+ for ( let i = 0 ; i < 256 ; i ++ ) {
323+ if ( Binary . getBit ( forkBitmap , i , 'LE' ) ) {
324+ const newFork = Fork . unmarshal ( reader , selfAddress . length )
325+ node . forks . set ( i , newFork )
326+ newFork . node . parent = node
327+ }
319328 }
320329 }
321330
@@ -331,6 +340,7 @@ export class MantarayNode {
331340 metadata ?: Record < string , string > | null ,
332341 ) {
333342 this . selfAddress = null
343+ this . type = null
334344 path = path instanceof Uint8Array ? path : ENCODER . encode ( path )
335345 debug ( 'adding fork' , { path : DECODER . decode ( path ) , reference : new Reference ( reference ) . represent ( ) } )
336346 // TODO: this should not be ignored
@@ -368,11 +378,13 @@ export class MantarayNode {
368378 tip . forks . set ( remainingPath [ 0 ] , fork )
369379 fork . node . parent = tip
370380 tip . selfAddress = null
381+ tip . type = null
371382 tip = newFork . node
372383 } else {
373384 tip . forks . set ( remainingPath [ 0 ] , newFork )
374385 newFork . node . parent = tip
375386 tip . selfAddress = null
387+ tip . type = null
376388 tip = newFork . node
377389 }
378390 }
@@ -383,6 +395,7 @@ export class MantarayNode {
383395 */
384396 removeFork ( path : string | Uint8Array ) {
385397 this . selfAddress = null
398+ this . type = null
386399 path = path instanceof Uint8Array ? path : ENCODER . encode ( path )
387400
388401 if ( path . length === 0 ) {
@@ -534,15 +547,22 @@ export class MantarayNode {
534547 determineType ( ) {
535548 let type = 0
536549
537- if ( ! Binary . equals ( this . targetAddress , NULL_ADDRESS ) || Binary . equals ( this . path , PATH_SEPARATOR ) ) {
550+ // Mirror Bee (pkg/manifest/mantaray/node.go): Add() marks every explicitly
551+ // added leaf as a value (makeValue), even one with a null entry such as a
552+ // metadata-only "/" node. In final-state terms a leaf (no forks) is always an
553+ // added entry, so it is a value; a node with forks is a value only when it also
554+ // carries an entry. The path-separator flag is set only when a separator occurs
555+ // past the first byte (IndexRune > 0), so a prefix that merely starts with '/'
556+ // does not qualify.
557+ if ( ! Binary . equals ( this . targetAddress , NULL_ADDRESS ) || this . forks . size === 0 ) {
538558 type |= TYPE_VALUE
539559 }
540560
541561 if ( this . forks . size > 0 ) {
542562 type |= TYPE_EDGE
543563 }
544564
545- if ( Binary . indexOf ( this . path , PATH_SEPARATOR ) !== - 1 && ! Binary . equals ( this . path , PATH_SEPARATOR ) ) {
565+ if ( Binary . indexOf ( this . path , PATH_SEPARATOR ) > 0 ) {
546566 type |= TYPE_WITH_PATH_SEPARATOR
547567 }
548568
0 commit comments