ff0af1ee unknown 1 month ago
1
2
3
4
5
6
7
8
9
10
use crate :: cbor:: { self , Value} ;
11
use crate :: hash:: { hash_object, ChangeHash, KeyId, ObjectRef, ObjectType} ;
12
use crate :: ids:: { ChangeId, NodeId} ;
01a47cd7 Andy McCall 1 month ago ↶
13
use crate :: lines:: Lines;
ff0af1ee unknown 1 month ago
14
use std:: collections:: BTreeMap;
15
16
pub const FORMAT_VERSION : u64 = 1 ;
17
6f8aeb99 Andy McCall 1 month ago ↶
18
0f93f49c Andy McCall 1 month ago ↶
19
20
21
22
23
pub const CHANGE_VERSION : u64 = 3 ;
6f8aeb99 Andy McCall 1 month ago ↶
24
0f93f49c Andy McCall 1 month ago ↶
25
26
# [ derive ( Clone, Copy, Debug, PartialEq, Eq, Hash ) ]
27
pub enum TextForm {
28
29
Cbor,
30
31
Raw,
32
33
34
CborFallback,
35
}
36
6f8aeb99 Andy McCall 1 month ago ↶
37
38
39
40
pub const TEXT_BLOB_MIN : usize = 4096 ;
41
ff0af1ee unknown 1 month ago
42
# [ derive ( Debug, Clone, PartialEq, Eq, thiserror::Error ) ]
43
pub enum ObjectError {
44
# [ error ( " cbor: {0}" ) ]
45
Cbor( # [ from ] cbor:: CborError) ,
46
# [ error ( " schema: {0}" ) ]
47
Schema( String ) ,
5552e69c Andy McCall 25 days ago ↶
48
49
50
# [ error ( " damaged index: {0}" ) ]
51
DamagedIndex( String ) ,
ff0af1ee unknown 1 month ago
52
}
53
54
pub ( crate ) fn schema < T> ( msg : impl Into< String> ) -> Result< T, ObjectError> {
55
Err ( ObjectError:: Schema( msg.into ( ) ) )
56
}
57
58
pub ( crate ) type Map = BTreeMap< u64 , Value> ;
59
60
pub ( crate ) fn field < 'a > ( m : & 'a Map, k : u64 , what : & str ) -> Result< & 'a Value, ObjectError> {
75167672 Andy McCall 1 month ago
61
m.get ( & k)
62
.ok_or_else ( | | ObjectError:: Schema( format! ( " missing field {k} ({what} )" ) ) )
ff0af1ee unknown 1 month ago
63
}
64
pub ( crate ) fn uint ( v : & Value, what : & str ) -> Result< u64 , ObjectError> {
75167672 Andy McCall 1 month ago
65
v.as_uint ( )
66
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected unsigned integer" ) ) )
ff0af1ee unknown 1 month ago
67
}
68
pub ( crate ) fn u32_ ( v : & Value, what : & str ) -> Result< u32 , ObjectError> {
69
u32 :: try_from( uint ( v, what) ? ) .map_err ( | _| ObjectError:: Schema( format! ( " {what} : exceeds u32" ) ) )
70
}
71
pub ( crate ) fn text < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a str , ObjectError> {
75167672 Andy McCall 1 month ago
72
v.as_text ( )
73
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected text" ) ) )
ff0af1ee unknown 1 month ago
74
}
75
pub ( crate ) fn array < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a [ Value] , ObjectError> {
75167672 Andy McCall 1 month ago
76
v.as_array ( )
77
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected array" ) ) )
ff0af1ee unknown 1 month ago
78
}
79
pub ( crate ) fn map < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a Map, ObjectError> {
75167672 Andy McCall 1 month ago
80
v.as_map ( )
81
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected map" ) ) )
ff0af1ee unknown 1 month ago
82
}
83
pub ( crate ) fn bytes < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a [ u8 ] , ObjectError> {
75167672 Andy McCall 1 month ago
84
v.as_bytes ( )
85
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected bytes" ) ) )
ff0af1ee unknown 1 month ago
86
}
87
pub ( crate ) fn oref ( v : & Value, what : & str ) -> Result< ObjectRef, ObjectError> {
75167672 Andy McCall 1 month ago
88
ObjectRef:: from_slice( bytes ( v, what) ? )
89
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected 32 bytes" ) ) )
ff0af1ee unknown 1 month ago
90
}
91
pub ( crate ) fn node_id ( v : & Value, what : & str ) -> Result< NodeId, ObjectError> {
75167672 Andy McCall 1 month ago
92
NodeId:: from_slice( bytes ( v, what) ? )
93
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected 16 bytes" ) ) )
ff0af1ee unknown 1 month ago
94
}
95
pub ( crate ) fn check_version ( m : & Map) -> Result< ( ) , ObjectError> {
96
let v = uint ( field ( m, 0 , " version" ) ? , " version" ) ? ;
97
if v ! = FORMAT_VERSION {
14edbaaf Andy McCall 1 month ago ↶
98
let exe = std:: env:: current_exe( )
99
.map ( | p | format! ( " at {} " , p.display ( ) ) )
100
.unwrap_or_default ( ) ;
101
let which = format! ( " oxy {} {exe} " , crate :: VERSION ) ;
102
return schema ( if v > FORMAT_VERSION {
103
format! (
104
" this data is format {v} , written by a newer oxy, and {which} reads format {FORMAT_VERSION} : install the current oxy, and check that no older copy comes first on PATH"
105
)
106
} else {
107
format! (
108
" this data is format {v} , written by an older oxy, and {which} reads format {FORMAT_VERSION} only"
109
)
110
} ) ;
ff0af1ee unknown 1 month ago
111
}
112
Ok ( ( ) )
113
}
114
115
116
117
118
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default ) ]
119
pub struct Hlc {
120
pub wall_ms : u64 ,
121
pub logical : u32 ,
122
}
123
124
impl Hlc {
125
pub fn now ( ) -> Hlc {
126
let wall_ms = std:: time:: SystemTime:: now( )
127
.duration_since ( std:: time:: UNIX_EPOCH )
128
.map ( | d | d.as_millis ( ) as u64 )
129
.unwrap_or ( 0 ) ;
75167672 Andy McCall 1 month ago
130
Hlc {
131
wall_ms,
132
logical: 0 ,
133
}
ff0af1ee unknown 1 month ago
134
}
135
pub ( crate ) fn to_value ( self ) -> Value {
75167672 Andy McCall 1 month ago
136
Value:: map( [
137
( 0 , Value:: Uint( self .wall_ms) ) ,
138
( 1 , Value:: Uint( self .logical as u64 ) ) ,
139
] )
ff0af1ee unknown 1 month ago
140
}
141
pub ( crate ) fn from_value ( v : & Value) -> Result< Hlc, ObjectError> {
142
let m = map ( v, " hlc" ) ? ;
143
Ok ( Hlc {
144
wall_ms: uint ( field ( m, 0 , " hlc.wall_ms" ) ? , " hlc.wall_ms" ) ? ,
145
logical: u32_ ( field ( m, 1 , " hlc.logical" ) ? , " hlc.logical" ) ? ,
146
} )
147
}
148
}
149
150
151
152
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
153
pub enum NodeKind {
154
File,
155
Dir,
156
Symlink,
4dee277c Andy McCall 1 month ago ↶
157
158
Subrepo,
ff0af1ee unknown 1 month ago
159
}
160
161
impl NodeKind {
252e8cb2 Andy McCall 1 month ago ↶
162
pub ( crate ) fn tag ( self ) -> u64 {
ff0af1ee unknown 1 month ago
163
match self {
164
NodeKind:: File => 0 ,
165
NodeKind:: Dir => 1 ,
166
NodeKind:: Symlink => 2 ,
4dee277c Andy McCall 1 month ago ↶
167
NodeKind:: Subrepo => 3 ,
ff0af1ee unknown 1 month ago
168
}
169
}
252e8cb2 Andy McCall 1 month ago ↶
170
pub ( crate ) fn from_tag ( t : u64 ) -> Result< NodeKind, ObjectError> {
ff0af1ee unknown 1 month ago
171
Ok ( match t {
172
0 => NodeKind:: File,
173
1 => NodeKind:: Dir,
174
2 => NodeKind:: Symlink,
4dee277c Andy McCall 1 month ago ↶
175
3 => NodeKind:: Subrepo,
ff0af1ee unknown 1 month ago
176
_ => return schema ( format! ( " unknown node kind {t} " ) ) ,
177
} )
178
}
179
}
180
181
182
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord ) ]
183
pub struct Mode {
184
pub exec : bool ,
185
pub opaque : bool ,
186
}
187
188
impl Mode {
75167672 Andy McCall 1 month ago
189
pub const TEXT : Mode = Mode {
190
exec: false ,
191
opaque: false ,
192
} ;
193
pub const OPAQUE : Mode = Mode {
194
exec: false ,
195
opaque: true ,
196
} ;
252e8cb2 Andy McCall 1 month ago ↶
197
pub ( crate ) fn bits ( self ) -> u64 {
ff0af1ee unknown 1 month ago
198
( self .exec as u64 ) | ( ( self .opaque as u64 ) < < 1 )
199
}
252e8cb2 Andy McCall 1 month ago ↶
200
pub ( crate ) fn from_bits ( b : u64 ) -> Result< Mode, ObjectError> {
ff0af1ee unknown 1 month ago
201
if b > 3 {
202
return schema ( format! ( " unknown mode bits {b} " ) ) ;
203
}
75167672 Andy McCall 1 month ago
204
Ok ( Mode {
205
exec: b & 1 ! = 0 ,
206
opaque: b & 2 ! = 0 ,
207
} )
ff0af1ee unknown 1 month ago
208
}
209
}
210
211
212
213
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord ) ]
214
pub struct OpRef {
215
pub change : ChangeHash,
216
pub index : u32 ,
217
}
218
219
impl OpRef {
220
fn to_value ( self ) -> Value {
75167672 Andy McCall 1 month ago
221
Value:: Array( vec! [
222
Value:: Bytes( self .change.0. to_vec ( ) ) ,
223
Value:: Uint( self .index as u64 ) ,
224
] )
ff0af1ee unknown 1 month ago
225
}
226
fn from_value ( v : & Value, what : & str ) -> Result< OpRef, ObjectError> {
227
let a = array ( v, what) ? ;
228
if a.len ( ) ! = 2 {
229
return schema ( format! ( " {what} : expected [hash, index]" ) ) ;
230
}
75167672 Andy McCall 1 month ago
231
Ok ( OpRef {
232
change: oref ( & a[ 0 ] , what) ? ,
233
index: u32_ ( & a[ 1 ] , what) ? ,
234
} )
ff0af1ee unknown 1 month ago
235
}
236
237
pub fn resolve ( self , me : ChangeHash) -> OpRef {
75167672 Andy McCall 1 month ago
238
if self .change.is_zero ( ) {
239
OpRef { change: me, .. self }
240
} else {
241
self
242
}
ff0af1ee unknown 1 month ago
243
}
244
}
245
246
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
247
pub enum TreeOp {
75167672 Andy McCall 1 month ago
248
AddNode {
249
node: NodeId,
250
kind: NodeKind,
251
mode: Mode,
252
parent: NodeId,
253
name: String ,
254
} ,
255
RemoveNode {
256
node: NodeId,
257
placement: OpRef,
258
} ,
259
Move {
260
node: NodeId,
261
old: OpRef,
262
parent: NodeId,
263
name: String ,
264
} ,
265
SetMode {
266
node: NodeId,
267
old: OpRef,
268
mode: Mode,
269
} ,
ff0af1ee unknown 1 month ago
270
}
271
272
273
pub fn validate_name ( name : & str ) -> Result< ( ) , ObjectError> {
274
if name.is_empty ( ) | | name = = " ." | | name = = " .." | | name.contains ( ' /' ) | | name.contains ( ' \0 ' ) {
275
return schema ( format! ( " invalid placement name {name:?} " ) ) ;
276
}
277
Ok ( ( ) )
278
}
279
280
impl TreeOp {
281
pub fn node ( & self ) -> NodeId {
282
match self {
283
TreeOp:: AddNode { node, .. }
284
| TreeOp:: RemoveNode { node, .. }
285
| TreeOp:: Move { node, .. }
286
| TreeOp:: SetMode { node, .. } => * node,
287
}
288
}
289
290
fn to_value ( & self ) -> Value {
291
let nid = | n : & NodeId| Value:: Bytes( n.0. to_vec ( ) ) ;
292
match self {
75167672 Andy McCall 1 month ago
293
TreeOp:: AddNode {
294
node,
295
kind,
296
mode,
297
parent,
298
name,
299
} => Value:: map( [
ff0af1ee unknown 1 month ago
300
( 0 , Value:: Uint( 0 ) ) ,
301
( 1 , nid ( node) ) ,
302
( 2 , Value:: Uint( kind.tag ( ) ) ) ,
303
( 3 , Value:: Uint( mode.bits ( ) ) ) ,
304
( 4 , nid ( parent) ) ,
305
( 5 , Value:: Text( name.clone ( ) ) ) ,
306
] ) ,
75167672 Andy McCall 1 month ago
307
TreeOp:: RemoveNode { node, placement } => Value:: map( [
308
( 0 , Value:: Uint( 1 ) ) ,
309
( 1 , nid ( node) ) ,
310
( 2 , placement.to_value ( ) ) ,
311
] ) ,
312
TreeOp:: Move {
313
node,
314
old,
315
parent,
316
name,
317
} => Value:: map( [
ff0af1ee unknown 1 month ago
318
( 0 , Value:: Uint( 2 ) ) ,
319
( 1 , nid ( node) ) ,
320
( 2 , old.to_value ( ) ) ,
321
( 4 , nid ( parent) ) ,
322
( 5 , Value:: Text( name.clone ( ) ) ) ,
323
] ) ,
324
TreeOp:: SetMode { node, old, mode } => Value:: map( [
325
( 0 , Value:: Uint( 3 ) ) ,
326
( 1 , nid ( node) ) ,
327
( 2 , old.to_value ( ) ) ,
328
( 3 , Value:: Uint( mode.bits ( ) ) ) ,
329
] ) ,
330
}
331
}
332
333
fn from_value ( v : & Value) -> Result< TreeOp, ObjectError> {
334
let m = map ( v, " tree_op" ) ? ;
335
let tag = uint ( field ( m, 0 , " tree_op tag" ) ? , " tree_op tag" ) ? ;
336
let node = node_id ( field ( m, 1 , " tree_op.node" ) ? , " tree_op.node" ) ? ;
337
let op = match tag {
338
0 => {
339
let name = text ( field ( m, 5 , " name" ) ? , " name" ) ? .to_owned ( ) ;
340
validate_name ( & name) ? ;
341
TreeOp:: AddNode {
342
node,
343
kind: NodeKind:: from_tag( uint ( field ( m, 2 , " kind" ) ? , " kind" ) ? ) ? ,
344
mode: Mode:: from_bits( uint ( field ( m, 3 , " mode" ) ? , " mode" ) ? ) ? ,
345
parent: node_id ( field ( m, 4 , " parent" ) ? , " parent" ) ? ,
346
name,
347
}
348
}
75167672 Andy McCall 1 month ago
349
1 => TreeOp:: RemoveNode {
350
node,
351
placement: OpRef:: from_value( field ( m, 2 , " placement" ) ? , " placement" ) ? ,
352
} ,
ff0af1ee unknown 1 month ago
353
2 => {
354
let name = text ( field ( m, 5 , " name" ) ? , " name" ) ? .to_owned ( ) ;
355
validate_name ( & name) ? ;
356
TreeOp:: Move {
357
node,
358
old: OpRef:: from_value( field ( m, 2 , " old" ) ? , " old" ) ? ,
359
parent: node_id ( field ( m, 4 , " parent" ) ? , " parent" ) ? ,
360
name,
361
}
362
}
363
3 => TreeOp:: SetMode {
364
node,
365
old: OpRef:: from_value( field ( m, 2 , " old" ) ? , " old" ) ? ,
366
mode: Mode:: from_bits( uint ( field ( m, 3 , " mode" ) ? , " mode" ) ? ) ? ,
367
} ,
368
_ => return schema ( format! ( " unknown tree op tag {tag} " ) ) ,
369
} ;
370
if node.is_root ( ) {
371
return schema ( " tree op on the root node" ) ;
372
}
373
Ok ( op)
374
}
375
}
376
377
378
379
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
380
pub enum AtomRef {
381
Start,
382
End,
383
Atom { change: ChangeHash, index: u32 } ,
384
}
385
386
impl AtomRef {
387
fn to_value ( self ) -> Value {
388
match self {
389
AtomRef:: Start => Value:: Uint( 0 ) ,
390
AtomRef:: End => Value:: Uint( 1 ) ,
75167672 Andy McCall 1 month ago
391
AtomRef:: Atom { change, index } => Value:: Array( vec! [
392
Value:: Bytes( change.0. to_vec ( ) ) ,
393
Value:: Uint( index as u64 ) ,
394
] ) ,
ff0af1ee unknown 1 month ago
395
}
396
}
397
fn from_value ( v : & Value, what : & str ) -> Result< AtomRef, ObjectError> {
398
match v {
399
Value:: Uint( 0 ) => Ok ( AtomRef:: Start) ,
400
Value:: Uint( 1 ) => Ok ( AtomRef:: End) ,
75167672 Andy McCall 1 month ago
401
Value:: Array( a) if a.len ( ) = = 2 => Ok ( AtomRef:: Atom {
402
change: oref ( & a[ 0 ] , what) ? ,
403
index: u32_ ( & a[ 1 ] , what) ? ,
404
} ) ,
ff0af1ee unknown 1 month ago
405
_ => schema ( format! ( " {what} : expected atom reference" ) ) ,
406
}
407
}
408
pub fn resolve ( self , me : ChangeHash) -> AtomRef {
409
match self {
75167672 Andy McCall 1 month ago
410
AtomRef:: Atom { change, index } if change.is_zero ( ) => {
411
AtomRef:: Atom { change: me, index }
412
}
ff0af1ee unknown 1 month ago
413
other => other,
414
}
415
}
416
}
417
418
419
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
420
pub struct AtomRange {
421
pub change : ChangeHash,
422
pub from : u32 ,
423
pub to : u32 ,
424
}
425
426
impl AtomRange {
427
fn to_value ( self ) -> Value {
428
Value:: Array( vec! [
429
Value:: Bytes( self .change.0. to_vec ( ) ) ,
430
Value:: Uint( self .from as u64 ) ,
431
Value:: Uint( self .to as u64 ) ,
432
] )
433
}
434
fn from_value ( v : & Value) -> Result< AtomRange, ObjectError> {
435
let a = array ( v, " atom range" ) ? ;
436
if a.len ( ) ! = 3 {
437
return schema ( " atom range: expected [hash, from, to]" ) ;
438
}
75167672 Andy McCall 1 month ago
439
let r = AtomRange {
440
change: oref ( & a[ 0 ] , " range.change" ) ? ,
441
from: u32_ ( & a[ 1 ] , " range.from" ) ? ,
442
to: u32_ ( & a[ 2 ] , " range.to" ) ? ,
443
} ;
ff0af1ee unknown 1 month ago
444
if r.from > = r.to {
445
return schema ( " atom range: empty or inverted" ) ;
446
}
447
Ok ( r)
448
}
449
pub fn resolve ( self , me : ChangeHash) -> AtomRange {
75167672 Andy McCall 1 month ago
450
if self .change.is_zero ( ) {
451
AtomRange { change: me, .. self }
452
} else {
453
self
454
}
ff0af1ee unknown 1 month ago
455
}
456
}
457
458
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
459
pub enum Edit {
75167672 Andy McCall 1 month ago
460
Insert {
461
node: NodeId,
462
after: AtomRef,
463
before: AtomRef,
01a47cd7 Andy McCall 1 month ago ↶
464
lines: crate :: lines:: Lines,
75167672 Andy McCall 1 month ago
465
} ,
466
Delete {
467
node: NodeId,
468
atoms: Vec< AtomRange> ,
469
} ,
470
Replace {
471
node: NodeId,
472
old: Option< ObjectRef> ,
473
new: Option< ObjectRef> ,
474
} ,
ff0af1ee unknown 1 month ago
475
}
476
477
impl Edit {
478
pub fn node ( & self ) -> NodeId {
479
match self {
75167672 Andy McCall 1 month ago
480
Edit:: Insert { node, .. } | Edit:: Delete { node, .. } | Edit:: Replace { node, .. } => {
481
* node
482
}
ff0af1ee unknown 1 month ago
483
}
6f8aeb99 Andy McCall 1 month ago ↶
484
}
485
486
487
fn stored_text ( & self , version : u64 ) -> bool {
488
matches! ( self , Edit:: Insert { lines, .. }
489
if version > = 2 & & cbor:: byte_strings_len( lines) > = TEXT_BLOB_MIN )
ff0af1ee unknown 1 month ago
490
}
491
192fad1e Andy McCall 1 month ago ↶
492
0f93f49c Andy McCall 1 month ago ↶
493
5b4c5130 Andy McCall 1 month ago ↶
494
495
496
fn to_value ( & self , stored : Option< ( ObjectRef, TextForm) > , compact : bool ) -> Value {
ff0af1ee unknown 1 month ago
497
let nid = | n : & NodeId| Value:: Bytes( n.0. to_vec ( ) ) ;
498
let opt = | o : & Option< ObjectRef> | match o {
499
Some ( r) => Value:: Bytes( r.0. to_vec ( ) ) ,
500
None => Value:: Null,
501
} ;
192fad1e Andy McCall 1 month ago ↶
502
match ( self , stored) {
503
(
504
Edit:: Insert {
505
node,
506
after,
507
before,
508
lines,
509
} ,
0f93f49c Andy McCall 1 month ago ↶
510
Some ( ( r, form) ) ,
511
) => {
512
let mut fields = vec! [
513
( 0 , Value:: Uint( 0 ) ) ,
514
( 1 , nid ( node) ) ,
515
( 2 , after.to_value ( ) ) ,
516
( 3 , before.to_value ( ) ) ,
517
( 5 , Value:: Bytes( r.0. to_vec ( ) ) ) ,
518
( 6 , Value:: Uint( lines.len ( ) as u64 ) ) ,
519
] ;
520
if form = = TextForm:: CborFallback {
521
fields.push ( ( 7 , Value:: Uint( 1 ) ) ) ;
522
}
523
Value:: map( fields)
524
}
192fad1e Andy McCall 1 month ago ↶
525
(
526
Edit:: Insert {
527
node,
528
after,
529
before,
530
lines,
531
} ,
532
None ,
533
) => Value:: map( [
534
( 0 , Value:: Uint( 0 ) ) ,
535
( 1 , nid ( node) ) ,
536
( 2 , after.to_value ( ) ) ,
537
( 3 , before.to_value ( ) ) ,
75167672 Andy McCall 1 month ago
538
(
539
4 ,
5b4c5130 Andy McCall 1 month ago ↶
540
if compact {
541
Value:: Encoded( cbor:: encode_byte_strings( lines) )
542
} else {
543
Value:: Array( lines.iter ( ) .map ( | l | Value:: Bytes( l.to_vec ( ) ) ) .collect ( ) )
544
} ,
75167672 Andy McCall 1 month ago
545
) ,
ff0af1ee unknown 1 month ago
546
] ) ,
192fad1e Andy McCall 1 month ago ↶
547
( Edit:: Delete { node, atoms } , _ ) => Value:: map( [
ff0af1ee unknown 1 month ago
548
( 0 , Value:: Uint( 1 ) ) ,
549
( 1 , nid ( node) ) ,
75167672 Andy McCall 1 month ago
550
(
551
2 ,
552
Value:: Array( atoms.iter ( ) .map ( | r | r.to_value ( ) ) .collect ( ) ) ,
553
) ,
ff0af1ee unknown 1 month ago
554
] ) ,
192fad1e Andy McCall 1 month ago ↶
555
( Edit:: Replace { node, old, new } , _ ) => Value:: map( [
75167672 Andy McCall 1 month ago
556
( 0 , Value:: Uint( 2 ) ) ,
557
( 1 , nid ( node) ) ,
558
( 2 , opt ( old) ) ,
559
( 3 , opt ( new) ) ,
560
] ) ,
ff0af1ee unknown 1 month ago
561
}
562
}
563
6f8aeb99 Andy McCall 1 month ago ↶
564
fn from_value ( v : & Value, version : u64 , resolve : & mut Resolver) -> Result< Edit, ObjectError> {
ff0af1ee unknown 1 month ago
565
let m = map ( v, " edit" ) ? ;
566
let tag = uint ( field ( m, 0 , " edit tag" ) ? , " edit tag" ) ? ;
567
let node = node_id ( field ( m, 1 , " edit.node" ) ? , " edit.node" ) ? ;
568
let opt = | v : & Value, what : & str | -> Result< Option< ObjectRef> , ObjectError> {
75167672 Andy McCall 1 month ago
569
if v.is_null ( ) {
570
Ok ( None )
571
} else {
572
Ok ( Some ( oref ( v, what) ? ) )
573
}
ff0af1ee unknown 1 month ago
574
} ;
575
Ok ( match tag {
576
0 => {
6f8aeb99 Andy McCall 1 month ago ↶
577
let lines = match ( m.get ( & 4 ) , m.get ( & 5 ) ) {
578
( Some ( inline) , None ) => {
01a47cd7 Andy McCall 1 month ago ↶
579
let lines = array_lines ( inline) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
580
if version > = 2 & & cbor:: byte_strings_len( & lines) > = TEXT_BLOB_MIN {
581
return schema ( " an insert this large is stored as a text blob in a version 2 change" ) ;
582
}
86e1824f Andy McCall 1 month ago ↶
583
if lines.is_empty ( ) {
584
return schema ( " insert with no lines" ) ;
585
}
6f8aeb99 Andy McCall 1 month ago ↶
586
lines
587
}
588
( None , Some ( text) ) if version > = 2 => {
589
let text = oref ( text, " insert text" ) ? ;
590
let count = u32_ ( field ( m, 6 , " insert line count" ) ? , " insert line count" ) ? ;
591
if count = = 0 {
592
return schema ( " insert with no lines" ) ;
593
}
0f93f49c Andy McCall 1 month ago ↶
594
let form = match ( version, m.get ( & 7 ) ) {
595
( 2 , None ) => TextForm:: Cbor,
596
( _ , None ) => TextForm:: Raw,
597
( 3 .. , Some ( Value:: Uint( 1 ) ) ) => TextForm:: CborFallback,
598
_ => return schema ( " unknown text blob form" ) ,
599
} ;
6f8aeb99 Andy McCall 1 month ago ↶
600
0f93f49c Andy McCall 1 month ago ↶
601
resolve ( & text, count, form) ?
6f8aeb99 Andy McCall 1 month ago ↶
602
}
603
_ => {
604
return schema (
605
" an insert has either its lines or, in a version 2 change, a text blob" ,
606
)
607
}
608
} ;
ff0af1ee unknown 1 month ago
609
Edit:: Insert {
610
node,
611
after: AtomRef:: from_value( field ( m, 2 , " after" ) ? , " after" ) ? ,
612
before: AtomRef:: from_value( field ( m, 3 , " before" ) ? , " before" ) ? ,
613
lines,
614
}
615
}
616
1 => {
617
let atoms = array ( field ( m, 2 , " atoms" ) ? , " atoms" ) ?
618
.iter ( )
619
.map ( AtomRange:: from_value)
620
.collect:: < Result< Vec< _ > , _ > > ( ) ? ;
621
if atoms.is_empty ( ) {
622
return schema ( " delete with no atoms" ) ;
623
}
624
Edit:: Delete { node, atoms }
625
}
75167672 Andy McCall 1 month ago
626
2 => Edit:: Replace {
627
node,
628
old: opt ( field ( m, 2 , " old" ) ? , " old" ) ? ,
629
new: opt ( field ( m, 3 , " new" ) ? , " new" ) ? ,
630
} ,
ff0af1ee unknown 1 month ago
631
_ => return schema ( format! ( " unknown edit tag {tag} " ) ) ,
632
} )
633
}
634
}
635
636
637
638
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
639
pub enum ChangeKind {
640
Normal,
641
Resolution,
642
WorkingCopy,
643
}
644
645
impl ChangeKind {
252e8cb2 Andy McCall 1 month ago ↶
646
pub ( crate ) fn tag ( self ) -> u64 {
ff0af1ee unknown 1 month ago
647
match self {
648
ChangeKind:: Normal => 0 ,
649
ChangeKind:: Resolution => 1 ,
650
ChangeKind:: WorkingCopy => 2 ,
651
}
652
}
252e8cb2 Andy McCall 1 month ago ↶
653
pub ( crate ) fn from_tag ( t : u64 ) -> Result< ChangeKind, ObjectError> {
ff0af1ee unknown 1 month ago
654
Ok ( match t {
655
0 => ChangeKind:: Normal,
656
1 => ChangeKind:: Resolution,
657
2 => ChangeKind:: WorkingCopy,
658
_ => return schema ( format! ( " unknown change kind {t} " ) ) ,
659
} )
660
}
661
}
662
663
664
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
665
pub struct Change {
6f8aeb99 Andy McCall 1 month ago ↶
666
667
668
pub version : u64 ,
ff0af1ee unknown 1 month ago
669
pub change_id : ChangeId,
670
pub deps : Vec< ChangeHash> ,
671
pub author : KeyId,
672
pub hlc : Hlc,
673
pub message : String,
674
pub tree_ops : Vec< TreeOp> ,
675
pub edits : Vec< Edit> ,
676
pub kind : ChangeKind,
677
}
678
679
impl Change {
680
pub fn normalize ( & mut self ) {
681
self .deps.sort ( ) ;
682
self .deps.dedup ( ) ;
683
}
684
685
pub fn to_value ( & self ) -> Value {
5b4c5130 Andy McCall 1 month ago ↶
686
self .value_with ( & mut | _ | true , false )
6f8aeb99 Andy McCall 1 month ago ↶
687
}
688
192fad1e Andy McCall 1 month ago ↶
689
690
691
692
0f93f49c Andy McCall 1 month ago ↶
693
fn stored_texts ( & self , sink : & mut TextSink) -> Vec< Option< ( ObjectRef, TextForm) > > {
192fad1e Andy McCall 1 month ago ↶
694
use rayon:: prelude:: * ;
0f93f49c Andy McCall 1 month ago ↶
695
let mut refs: Vec< Option< ( ObjectRef, TextForm) > > = vec! [ None ; self .edits.len ( ) ] ;
192fad1e Andy McCall 1 month ago ↶
696
let stored: Vec< usize > = ( 0 .. self .edits.len ( ) )
697
.filter ( | i | self .edits[ * i] .stored_text ( self .version) )
698
.collect ( ) ;
5b4c5130 Andy McCall 1 month ago ↶
699
700
701
702
let mut batches: Vec< & [ usize ] > = Vec :: new( ) ;
703
let ( mut start, mut bytes) = ( 0 , 0 usize ) ;
704
for ( k, i) in stored.iter ( ) .enumerate ( ) {
705
if let Edit:: Insert { lines, .. } = & self .edits[ * i] {
706
bytes + = lines.bytes ( ) .len ( ) ;
707
}
708
if k + 1 - start = = 256 | | bytes > = 32 < < 20 {
709
batches.push ( & stored[ start.. = k] ) ;
710
( start, bytes) = ( k + 1 , 0 ) ;
711
}
712
}
713
if start < stored.len ( ) {
714
batches.push ( & stored[ start.. ] ) ;
715
}
716
for batch in batches {
192fad1e Andy McCall 1 month ago ↶
717
let made: Vec< StoredText> = batch
718
.par_iter ( )
719
.map ( | i | {
720
let Edit:: Insert { lines, .. } = & self .edits[ * i] else {
721
unreachable! ( " only inserts are stored as text" ) ;
0f93f49c Andy McCall 1 month ago ↶
722
} ;
723
let form = text_form ( self .version, lines) ;
724
let content = match form {
725
TextForm:: Raw => lines.bytes ( ) .to_vec ( ) ,
726
TextForm:: Cbor | TextForm:: CborFallback => cbor:: encode_byte_strings( lines) ,
192fad1e Andy McCall 1 month ago ↶
727
} ;
728
StoredText {
729
blob: Blob:: of( & content) ,
730
content,
0f93f49c Andy McCall 1 month ago ↶
731
form,
192fad1e Andy McCall 1 month ago ↶
732
}
733
} )
734
.collect ( ) ;
17f94b13 Andy McCall 1 month ago ↶
735
for ( i, t) in batch.iter ( ) .zip ( & made) {
0f93f49c Andy McCall 1 month ago ↶
736
refs[ * i] = Some ( ( t.blob.hash ( ) , t.form) ) ;
17f94b13 Andy McCall 1 month ago ↶
737
}
738
if ! sink ( made) {
739
break ;
192fad1e Andy McCall 1 month ago ↶
740
}
741
}
742
refs
743
}
744
5b4c5130 Andy McCall 1 month ago ↶
745
746
747
fn value_with ( & self , sink : & mut TextSink, compact : bool ) -> Value {
ff0af1ee unknown 1 month ago
748
Value:: map( [
6f8aeb99 Andy McCall 1 month ago ↶
749
( 0 , Value:: Uint( self .version) ) ,
ff0af1ee unknown 1 month ago
750
( 1 , Value:: Bytes( self .change_id.0. to_vec ( ) ) ) ,
75167672 Andy McCall 1 month ago
751
(
752
2 ,
753
Value:: Array(
754
self .deps
755
.iter ( )
756
.map ( | d | Value:: Bytes( d.0. to_vec ( ) ) )
757
.collect ( ) ,
758
) ,
759
) ,
ff0af1ee unknown 1 month ago
760
( 3 , Value:: Bytes( self .author.0. to_vec ( ) ) ) ,
761
( 4 , self .hlc.to_value ( ) ) ,
762
( 5 , Value:: Text( self .message.clone ( ) ) ) ,
75167672 Andy McCall 1 month ago
763
(
764
6 ,
765
Value:: Array( self .tree_ops.iter ( ) .map ( | t | t.to_value ( ) ) .collect ( ) ) ,
766
) ,
767
(
768
7 ,
6f8aeb99 Andy McCall 1 month ago ↶
769
Value:: Array(
770
self .edits
771
.iter ( )
17f94b13 Andy McCall 1 month ago ↶
772
.zip ( self .stored_texts ( sink) )
5b4c5130 Andy McCall 1 month ago ↶
773
.map ( | ( e , stored ) | e.to_value ( stored, compact) )
6f8aeb99 Andy McCall 1 month ago ↶
774
.collect ( ) ,
775
) ,
75167672 Andy McCall 1 month ago
776
) ,
ff0af1ee unknown 1 month ago
777
( 8 , Value:: Uint( self .kind.tag ( ) ) ) ,
778
( 9 , Value:: Map( BTreeMap:: new( ) ) ) ,
779
] )
780
}
781
782
pub fn encode ( & self ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
783
debug_assert! (
784
self .deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ,
785
" deps must be normalized"
786
) ;
5b4c5130 Andy McCall 1 month ago ↶
787
cbor:: encode( & self .value_with ( & mut | _ | true , true ) )
ff0af1ee unknown 1 month ago
788
}
789
6f8aeb99 Andy McCall 1 month ago ↶
790
791
792
pub fn encode_parts ( & self ) -> ( Vec< u8 > , Vec< StoredText> ) {
793
debug_assert! (
794
self .deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ,
795
" deps must be normalized"
796
) ;
797
let mut texts = Vec :: new( ) ;
5b4c5130 Andy McCall 1 month ago ↶
798
let bytes = cbor:: encode( & self .value_with (
799
& mut | batch| {
800
texts.extend ( batch) ;
801
true
802
} ,
803
true ,
804
) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
805
( bytes, texts)
17f94b13 Andy McCall 1 month ago ↶
806
}
807
808
809
810
811
pub fn encode_streaming < E> (
812
& self ,
813
mut store : impl FnMut( Vec <StoredText >) -> Result< ( ) , E> ,
814
) -> Result< Vec< u8 > , E> {
815
debug_assert! (
816
self .deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ,
817
" deps must be normalized"
818
) ;
819
let mut failed = None ;
5b4c5130 Andy McCall 1 month ago ↶
820
let value = self .value_with (
821
& mut | batch| match store ( batch) {
822
Ok ( ( ) ) => true ,
823
Err ( e) => {
824
failed = Some ( e) ;
825
false
826
}
827
} ,
828
true ,
829
) ;
17f94b13 Andy McCall 1 month ago ↶
830
match failed {
831
Some ( e) => Err ( e) ,
832
None => Ok ( cbor:: encode( & value) ) ,
833
}
6f8aeb99 Andy McCall 1 month ago ↶
834
}
835
836
837
pub fn has_stored_text ( & self ) -> bool {
838
self .edits.iter ( ) .any ( | e | e.stored_text ( self .version) )
839
}
840
ff0af1ee unknown 1 month ago
841
pub fn hash ( & self ) -> ChangeHash {
842
hash_object ( ObjectType:: Change, & self .encode ( ) )
843
}
844
6f8aeb99 Andy McCall 1 month ago ↶
845
846
ff0af1ee unknown 1 month ago
847
pub fn decode ( bytes : & [u8 ]) -> Result< Change, ObjectError> {
0f93f49c Andy McCall 1 month ago ↶
848
Self :: decode_with( bytes, & mut | r, _ , _ | {
6f8aeb99 Andy McCall 1 month ago ↶
849
schema ( format! (
850
" text blob {} needs a store to read it from" ,
851
r.short ( 12 )
852
) )
853
} )
854
}
855
856
857
858
pub fn decode_with ( bytes : & [u8 ], resolve : & mut Resolver) -> Result< Change, ObjectError> {
ff0af1ee unknown 1 month ago
859
let v = cbor:: decode( bytes) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
860
Self :: from_value_with( & v, resolve)
ff0af1ee unknown 1 month ago
861
}
862
863
pub fn from_value ( v : & Value) -> Result< Change, ObjectError> {
0f93f49c Andy McCall 1 month ago ↶
864
Self :: from_value_with( v, & mut | r, _ , _ | {
6f8aeb99 Andy McCall 1 month ago ↶
865
schema ( format! (
866
" text blob {} needs a store to read it from" ,
867
r.short ( 12 )
868
) )
869
} )
870
}
871
a4fad5b0 Andy McCall 1 month ago ↶
872
pub ( crate ) fn from_value_with (
873
v : & Value,
874
resolve : & mut Resolver,
875
) -> Result< Change, ObjectError> {
ff0af1ee unknown 1 month ago
876
let m = map ( v, " change" ) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
877
let version = uint ( field ( m, 0 , " version" ) ? , " version" ) ? ;
878
if ! ( 1 .. = CHANGE_VERSION ) .contains ( & version) {
879
return schema ( format! ( " unsupported change format version {version} " ) ) ;
880
}
ff0af1ee unknown 1 month ago
881
let change_id = ChangeId:: from_slice( bytes ( field ( m, 1 , " change_id" ) ? , " change_id" ) ? )
882
.ok_or_else ( | | ObjectError:: Schema( " change_id: expected 16 bytes" .into ( ) ) ) ? ;
883
let deps = array ( field ( m, 2 , " deps" ) ? , " deps" ) ?
884
.iter ( )
885
.map ( | d | oref ( d, " dep" ) )
886
.collect:: < Result< Vec< _ > , _ > > ( ) ? ;
887
if ! deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) {
888
return schema ( " deps must be sorted and unique" ) ;
889
}
890
if deps.iter ( ) .any ( | d | d.is_zero ( ) ) {
891
return schema ( " deps may not contain the self reference" ) ;
892
}
893
let extra = map ( field ( m, 9 , " extra" ) ? , " extra" ) ? ;
894
if ! extra.is_empty ( ) {
895
return schema ( " extra must be empty in format version 1" ) ;
896
}
897
Ok ( Change {
6f8aeb99 Andy McCall 1 month ago ↶
898
version,
ff0af1ee unknown 1 month ago
899
change_id,
900
deps,
901
author: oref ( field ( m, 3 , " author" ) ? , " author" ) ? ,
902
hlc: Hlc:: from_value( field ( m, 4 , " hlc" ) ? ) ? ,
903
message: text ( field ( m, 5 , " message" ) ? , " message" ) ? .to_owned ( ) ,
904
tree_ops: array ( field ( m, 6 , " tree_ops" ) ? , " tree_ops" ) ?
905
.iter ( )
906
.map ( TreeOp:: from_value)
907
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
908
edits: array ( field ( m, 7 , " edits" ) ? , " edits" ) ?
909
.iter ( )
6f8aeb99 Andy McCall 1 month ago ↶
910
.map ( | e | Edit:: from_value( e, version, resolve) )
ff0af1ee unknown 1 month ago
911
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
912
kind: ChangeKind:: from_tag( uint ( field ( m, 8 , " kind" ) ? , " kind" ) ? ) ? ,
913
} )
914
}
915
}
916
6f8aeb99 Andy McCall 1 month ago ↶
917
918
86e1824f Andy McCall 1 month ago ↶
919
920
0f93f49c Andy McCall 1 month ago ↶
921
pub type Resolver < 'a > = dyn FnMut ( & ObjectRef, u32 , TextForm) -> Result< Lines, ObjectError> + 'a ;
17f94b13 Andy McCall 1 month ago ↶
922
923
924
925
type TextSink < 'a > = dyn FnMut ( Vec< StoredText> ) -> bool + 'a ;
a4fad5b0 Andy McCall 1 month ago ↶
926
927
928
929
# [ derive ( Clone, Copy, Debug, PartialEq, Eq ) ]
930
pub enum TextRef {
0f93f49c Andy McCall 1 month ago ↶
931
932
Blob( ObjectRef, u32 , TextForm) ,
a4fad5b0 Andy McCall 1 month ago ↶
933
934
935
Inline {
936
change: ChangeHash,
937
start: u32 ,
938
end: u32 ,
939
} ,
940
}
941
942
943
01a47cd7 Andy McCall 1 month ago ↶
944
pub fn inline_lines ( array : & [u8 ]) -> Result< Lines, ObjectError> {
a4fad5b0 Andy McCall 1 month ago ↶
945
let v = cbor:: decode( array) ? ;
946
let lines = array_lines ( & v) ? ;
947
if lines.is_empty ( ) {
948
return schema ( " insert with no lines" ) ;
949
}
950
Ok ( lines)
951
}
952
01a47cd7 Andy McCall 1 month ago ↶
953
954
fn array_lines ( v : & Value) -> Result< Lines, ObjectError> {
955
Ok ( array ( v, " lines" ) ?
a4fad5b0 Andy McCall 1 month ago ↶
956
.iter ( )
01a47cd7 Andy McCall 1 month ago ↶
957
.map ( | l | bytes ( l, " line" ) )
958
.collect:: < Result< Vec< & [ u8 ] > , _ > > ( ) ?
959
.into_iter ( )
960
.collect ( ) )
a4fad5b0 Andy McCall 1 month ago ↶
961
}
962
963
impl Change {
964
965
966
967
968
969
pub fn decode_skeleton (
970
bytes : & [u8 ],
971
me : ChangeHash,
972
) -> Result< ( Change, Vec< TextRef> ) , ObjectError> {
973
let mut c = cbor:: Cursor:: new( bytes) ;
974
let ( major, n) = c.head ( ) ? ;
975
if major ! = 5 {
976
return schema ( " change: expected map" ) ;
977
}
978
let mut fields = Map:: new( ) ;
979
let mut edits = Vec :: new( ) ;
980
let mut texts = Vec :: new( ) ;
981
let mut version = None ;
982
let mut last: Option< u64 > = None ;
983
for _ in 0 .. n {
984
let k = map_key ( & mut c, & mut last) ? ;
985
if k = = 7 {
986
let Some ( v) = version else {
987
return schema ( " change: version must come first" ) ;
988
} ;
989
let ( am, an) = c.head ( ) ? ;
990
if am ! = 4 {
991
return schema ( " edits: expected array" ) ;
992
}
993
for _ in 0 .. an {
994
edits.push ( edit_skeleton ( & mut c, v, me, & mut texts) ? ) ;
995
}
996
fields.insert ( 7 , Value:: Array( Vec :: new( ) ) ) ;
997
} else {
998
let val = c.value ( ) ? ;
999
if k = = 0 {
1000
version = Some ( uint ( & val, " version" ) ? ) ;
1001
}
1002
fields.insert ( k, val) ;
1003
}
1004
}
1005
if ! c.at_end ( ) {
1006
return Err ( ObjectError:: Cbor( cbor:: CborError:: TrailingBytes) ) ;
1007
}
1008
let mut change = Change:: from_value( & Value:: Map( fields) ) ? ;
1009
change.edits = edits;
1010
Ok ( ( change, texts) )
1011
}
1012
}
1013
4d6c9c1b Andy McCall 1 month ago ↶
1014
impl Change {
1015
1016
1017
1018
pub fn decode_meta ( bytes : & [u8 ]) -> Result< crate:: state:: ChangeMeta, ObjectError> {
1019
let mut c = cbor:: Cursor:: new( bytes) ;
1020
let ( major, n) = c.head ( ) ? ;
1021
if major ! = 5 {
1022
return schema ( " change: expected map" ) ;
1023
}
1024
let mut fields = Map:: new( ) ;
1025
let mut last: Option< u64 > = None ;
1026
for _ in 0 .. n {
1027
let k = map_key ( & mut c, & mut last) ? ;
1028
if k = = 6 | | k = = 7 {
1029
c.skip ( ) ? ;
1030
fields.insert ( k, Value:: Array( Vec :: new( ) ) ) ;
1031
} else {
1032
fields.insert ( k, c.value ( ) ? ) ;
1033
}
1034
}
1035
if ! c.at_end ( ) {
1036
return Err ( ObjectError:: Cbor( cbor:: CborError:: TrailingBytes) ) ;
1037
}
1038
Ok ( crate :: state:: ChangeMeta:: of( & Change:: from_value(
1039
& Value:: Map( fields) ,
1040
) ? ) )
1041
}
1042
}
1043
a4fad5b0 Andy McCall 1 month ago ↶
1044
1045
fn map_key ( c : & mut cbor:: Cursor, last : & mut Option< u64 > ) -> Result< u64 , ObjectError> {
1046
let ( km, k) = c.head ( ) ? ;
1047
if km ! = 0 | | last.is_some_and ( | l | k < = l ) {
1048
return Err ( ObjectError:: Cbor( cbor:: CborError:: BadMapKey) ) ;
1049
}
1050
* last = Some ( k) ;
1051
Ok ( k)
1052
}
1053
1054
1055
fn edit_skeleton (
1056
c : & mut cbor:: Cursor,
1057
version : u64 ,
1058
me : ChangeHash,
1059
texts : & mut Vec< TextRef> ,
1060
) -> Result< Edit, ObjectError> {
1061
let ( major, n) = c.head ( ) ? ;
1062
if major ! = 5 {
1063
return schema ( " edit: expected map" ) ;
1064
}
1065
let mut fields = Map:: new( ) ;
1066
let mut inline: Option< ( usize , usize , u64 ) > = None ;
1067
let mut last: Option< u64 > = None ;
1068
for _ in 0 .. n {
1069
let k = map_key ( c, & mut last) ? ;
1070
if k = = 4 {
1071
let start = c.pos ( ) ;
1072
let ( am, count) = c.head ( ) ? ;
1073
if am ! = 4 {
1074
return schema ( " lines: expected array" ) ;
1075
}
1076
for _ in 0 .. count {
1077
c.skip ( ) ? ;
1078
}
1079
inline = Some ( ( start, c.pos ( ) , count) ) ;
1080
continue ;
1081
}
1082
fields.insert ( k, c.value ( ) ? ) ;
1083
}
1084
let Some ( ( start, end, count) ) = inline else {
0f93f49c Andy McCall 1 month ago ↶
1085
return Edit:: from_value( & Value:: Map( fields) , version, & mut | text, count, form| {
1086
texts.push ( TextRef:: Blob( * text, count, form) ) ;
01a47cd7 Andy McCall 1 month ago ↶
1087
Ok ( Lines:: default( ) )
a4fad5b0 Andy McCall 1 month ago ↶
1088
} ) ;
1089
} ;
1090
if uint ( field ( & fields, 0 , " edit tag" ) ? , " edit tag" ) ? ! = 0 | | fields.contains_key ( & 5 ) {
1091
return schema ( " only an insert has inline lines, and not with a text blob" ) ;
1092
}
1093
if count = = 0 {
1094
return schema ( " insert with no lines" ) ;
1095
}
1096
if version > = 2 & & end - start > = TEXT_BLOB_MIN {
1097
return schema ( " an insert this large is stored as a text blob in a version 2 change" ) ;
1098
}
1099
let ( Ok ( start) , Ok ( end) ) = ( u32 :: try_from( start) , u32 :: try_from( end) ) else {
1100
return schema ( " change too large" ) ;
1101
} ;
1102
texts.push ( TextRef:: Inline {
1103
change: me,
1104
start,
1105
end,
1106
} ) ;
1107
Ok ( Edit:: Insert {
1108
node: node_id ( field ( & fields, 1 , " edit.node" ) ? , " edit.node" ) ? ,
1109
after: AtomRef:: from_value( field ( & fields, 2 , " after" ) ? , " after" ) ? ,
1110
before: AtomRef:: from_value( field ( & fields, 3 , " before" ) ? , " before" ) ? ,
01a47cd7 Andy McCall 1 month ago ↶
1111
lines: Lines:: default( ) ,
a4fad5b0 Andy McCall 1 month ago ↶
1112
} )
1113
}
6f8aeb99 Andy McCall 1 month ago ↶
1114
1115
1116
1117
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
1118
pub struct StoredText {
1119
pub blob : Blob,
1120
pub content : Vec< u8 > ,
0f93f49c Andy McCall 1 month ago ↶
1121
pub form : TextForm,
6f8aeb99 Andy McCall 1 month ago ↶
1122
}
1123
0f93f49c Andy McCall 1 month ago ↶
1124
1125
fn text_form ( version : u64 , lines : & Lines) -> TextForm {
1126
match version {
1127
.. = 2 => TextForm:: Cbor,
1128
_ if lines.is_split_form ( ) => TextForm:: Raw,
1129
_ => TextForm:: CborFallback,
1130
}
1131
}
1132
6f8aeb99 Andy McCall 1 month ago ↶
1133
1134
1135
0f93f49c Andy McCall 1 month ago ↶
1136
pub fn text_lines ( content : & [u8 ], count : u32 , form : TextForm) -> Result< Lines, ObjectError> {
1137
let lines = match form {
1138
TextForm:: Raw => Lines:: split( content) ,
1139
TextForm:: Cbor | TextForm:: CborFallback => array_lines ( & cbor:: decode( content) ? ) ? ,
1140
} ;
1141
1142
1143
if cbor:: byte_strings_len( & lines) < TEXT_BLOB_MIN {
6f8aeb99 Andy McCall 1 month ago ↶
1144
return schema ( format! (
1145
" a text blob of {} bytes is short enough to be inline" ,
1146
content.len ( )
1147
) ) ;
1148
}
0f93f49c Andy McCall 1 month ago ↶
1149
if form = = TextForm:: CborFallback & & lines.is_split_form ( ) {
1150
return schema ( " these lines are stored as their bytes in a version 3 change" ) ;
1151
}
6f8aeb99 Andy McCall 1 month ago ↶
1152
if lines.len ( ) ! = count as usize {
1153
return schema ( format! (
1154
" a text blob holds {} lines, the insert says {count} " ,
1155
lines.len ( )
1156
) ) ;
1157
}
1158
Ok ( lines)
1159
}
1160
ff0af1ee unknown 1 month ago
1161
1162
1163
1164
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
1165
pub struct Blob {
1166
pub length : u64 ,
1167
pub chunks : Vec< ObjectRef> ,
1168
}
1169
1170
impl Blob {
6f8aeb99 Andy McCall 1 month ago ↶
1171
1172
1173
pub fn of ( content : & [u8 ]) -> Blob {
1174
Blob {
1175
length: content.len ( ) as u64 ,
1176
chunks: crate :: chunk:: chunks( content)
1177
.into_iter ( )
1178
.map ( | c | hash_object ( ObjectType:: Chunk, c) )
1179
.collect ( ) ,
1180
}
1181
}
1182
ff0af1ee unknown 1 month ago
1183
pub fn encode ( & self ) -> Vec< u8 > {
1184
cbor:: encode( & Value:: map( [
1185
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
1186
( 1 , Value:: Uint( self .length) ) ,
75167672 Andy McCall 1 month ago
1187
(
1188
2 ,
1189
Value:: Array(
1190
self .chunks
1191
.iter ( )
1192
.map ( | c | Value:: Bytes( c.0. to_vec ( ) ) )
1193
.collect ( ) ,
1194
) ,
1195
) ,
ff0af1ee unknown 1 month ago
1196
] ) )
1197
}
1198
pub fn hash ( & self ) -> ObjectRef {
1199
hash_object ( ObjectType:: Blob, & self .encode ( ) )
1200
}
1201
pub fn decode ( bytes : & [u8 ]) -> Result< Blob, ObjectError> {
1202
let v = cbor:: decode( bytes) ? ;
1203
let m = map ( & v, " blob" ) ? ;
1204
check_version ( m) ? ;
1205
Ok ( Blob {
1206
length: uint ( field ( m, 1 , " length" ) ? , " length" ) ? ,
1207
chunks: array ( field ( m, 2 , " chunks" ) ? , " chunks" ) ?
1208
.iter ( )
1209
.map ( | c | oref ( c, " chunk" ) )
1210
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
1211
} )
1212
}
1213
}
1214
252e8cb2 Andy McCall 1 month ago ↶
1215
1216
1217
1218
1219
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
1220
pub struct Checkpoint {
1221
pub heads : Vec< ChangeHash> ,
1222
pub count : u64 ,
1223
pub payload : ObjectRef,
1224
}
1225
1226
impl Checkpoint {
1227
pub fn encode ( & self ) -> Vec< u8 > {
1228
cbor:: encode( & Value:: map( [
1229
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
75167672 Andy McCall 1 month ago
1230
(
1231
1 ,
1232
Value:: Array(
1233
self .heads
1234
.iter ( )
1235
.map ( | h | Value:: Bytes( h.0. to_vec ( ) ) )
1236
.collect ( ) ,
1237
) ,
1238
) ,
252e8cb2 Andy McCall 1 month ago ↶
1239
( 2 , Value:: Uint( self .count) ) ,
1240
( 3 , Value:: Bytes( self .payload.0. to_vec ( ) ) ) ,
1241
] ) )
1242
}
1243
pub fn hash ( & self ) -> ObjectRef {
1244
hash_object ( ObjectType:: Checkpoint, & self .encode ( ) )
1245
}
1246
pub fn decode ( bytes : & [u8 ]) -> Result< Checkpoint, ObjectError> {
1247
let v = cbor:: decode( bytes) ? ;
1248
let m = map ( & v, " checkpoint" ) ? ;
1249
check_version ( m) ? ;
1250
Ok ( Checkpoint {
75167672 Andy McCall 1 month ago
1251
heads: array ( field ( m, 1 , " heads" ) ? , " heads" ) ?
1252
.iter ( )
1253
.map ( | h | oref ( h, " head" ) )
1254
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
252e8cb2 Andy McCall 1 month ago ↶
1255
count: uint ( field ( m, 2 , " count" ) ? , " count" ) ? ,
1256
payload: oref ( field ( m, 3 , " payload" ) ? , " payload" ) ? ,
1257
} )
1258
}
1259
}
1260
86e682c2 Andy McCall 1 month ago ↶
1261
1262
1263
1264
# [ derive ( Debug, Clone, PartialEq, Eq, Default ) ]
1265
pub struct StateObject {
1266
pub heads : Vec< ChangeHash> ,
1267
}
1268
1269
impl StateObject {
1270
pub fn new ( mut heads : Vec< ChangeHash> ) -> StateObject {
1271
heads.sort ( ) ;
1272
heads.dedup ( ) ;
1273
StateObject { heads }
1274
}
1275
pub fn encode ( & self ) -> Vec< u8 > {
1276
debug_assert! ( self .heads.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ) ;
1277
cbor:: encode( & Value:: map( [
1278
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
75167672 Andy McCall 1 month ago
1279
(
1280
1 ,
1281
Value:: Array(
1282
self .heads
1283
.iter ( )
1284
.map ( | h | Value:: Bytes( h.0. to_vec ( ) ) )
1285
.collect ( ) ,
1286
) ,
1287
) ,
86e682c2 Andy McCall 1 month ago ↶
1288
] ) )
1289
}
1290
pub fn hash ( & self ) -> ObjectRef {
1291
hash_object ( ObjectType:: State, & self .encode ( ) )
1292
}
1293
pub fn decode ( bytes : & [u8 ]) -> Result< StateObject, ObjectError> {
1294
let v = cbor:: decode( bytes) ? ;
1295
let m = map ( & v, " state" ) ? ;
1296
check_version ( m) ? ;
75167672 Andy McCall 1 month ago
1297
let heads = array ( field ( m, 1 , " heads" ) ? , " heads" ) ?
1298
.iter ( )
1299
.map ( | h | oref ( h, " head" ) )
1300
.collect:: < Result< Vec< _ > , _ > > ( ) ? ;
86e682c2 Andy McCall 1 month ago ↶
1301
if ! heads.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) {
1302
return schema ( " state heads must be sorted and unique" ) ;
1303
}
1304
Ok ( StateObject { heads } )
1305
}
1306
}
1307
ff0af1ee unknown 1 month ago
1308
# [ cfg ( test ) ]
1309
pub ( crate ) mod tests {
1310
use super :: * ;
1311
1312
pub ( crate ) fn sample_change ( ) -> Change {
1313
let n = NodeId( [ 7 ; 16 ] ) ;
1314
let dep = ObjectRef( [ 1 ; 32 ] ) ;
1315
let mut c = Change {
6f8aeb99 Andy McCall 1 month ago ↶
1316
version: crate :: objects:: CHANGE_VERSION ,
ff0af1ee unknown 1 month ago
1317
change_id: ChangeId( [ 9 ; 16 ] ) ,
1318
deps: vec! [ ObjectRef( [ 2 ; 32 ] ) , dep] ,
1319
author: ObjectRef( [ 3 ; 32 ] ) ,
75167672 Andy McCall 1 month ago
1320
hlc: Hlc {
1321
wall_ms: 1_700_000_000_000 ,
1322
logical: 4 ,
1323
} ,
ff0af1ee unknown 1 month ago
1324
message: " Add greeting" .into ( ) ,
1325
tree_ops: vec! [
75167672 Andy McCall 1 month ago
1326
TreeOp:: AddNode {
1327
node: n,
1328
kind: NodeKind:: File,
1329
mode: Mode:: TEXT ,
1330
parent: NodeId:: ROOT ,
1331
name: " hello.txt" .into ( ) ,
1332
} ,
1333
TreeOp:: SetMode {
1334
node: n,
1335
old: OpRef {
1336
change: ObjectRef:: ZERO ,
1337
index: 0 ,
1338
} ,
1339
mode: Mode {
1340
exec: true ,
1341
opaque: false ,
1342
} ,
1343
} ,
ff0af1ee unknown 1 month ago
1344
] ,
1345
edits: vec! [
75167672 Andy McCall 1 month ago
1346
Edit:: Insert {
1347
node: n,
1348
after: AtomRef:: Start,
1349
before: AtomRef:: End,
01a47cd7 Andy McCall 1 month ago ↶
1350
lines: vec! [ b " hello\n " .to_vec ( ) , b " world\n " .to_vec ( ) ] .into ( ) ,
75167672 Andy McCall 1 month ago
1351
} ,
1352
Edit:: Delete {
1353
node: n,
1354
atoms: vec! [ AtomRange {
1355
change: dep,
1356
from: 0 ,
1357
to: 2 ,
1358
} ] ,
1359
} ,
1360
Edit:: Replace {
1361
node: n,
1362
old: None ,
1363
new: Some ( ObjectRef( [ 5 ; 32 ] ) ) ,
1364
} ,
ff0af1ee unknown 1 month ago
1365
] ,
1366
kind: ChangeKind:: Normal,
1367
} ;
1368
c.normalize ( ) ;
1369
c
1370
}
1371
1372
# [ test ]
1373
fn change_round_trips_and_hash_is_stable ( ) {
1374
let c = sample_change ( ) ;
1375
let bytes = c.encode ( ) ;
1376
let d = Change:: decode( & bytes) .unwrap ( ) ;
1377
assert_eq! ( c, d) ;
1378
assert_eq! ( c.hash ( ) , d.hash ( ) ) ;
1379
assert_eq! ( d.encode ( ) , bytes) ;
1380
1381
let mut e = c.clone ( ) ;
1382
e.message.push ( ' !' ) ;
1383
assert_ne! ( e.hash ( ) , c.hash ( ) ) ;
5b4c5130 Andy McCall 1 month ago ↶
1384
}
1385
1386
# [ test ]
1387
fn compact_encoding_is_the_plain_one ( ) {
1388
1389
1390
let c = sample_change ( ) ;
1391
assert! ( ! c.has_stored_text ( ) , " the sample's insert is inline" ) ;
1392
let plain = cbor:: encode( & c.to_value ( ) ) ;
1393
assert_eq! ( c.encode ( ) , plain) ;
1394
assert_eq! ( c.encode_parts ( ) .0 , plain) ;
1395
let streamed: Result< Vec< u8 > , ( ) > = c.encode_streaming ( | _| Ok ( ( ) ) ) ;
1396
assert_eq! ( streamed.unwrap ( ) , plain) ;
1397
assert_eq! ( Change:: decode( & plain) .unwrap ( ) , c) ;
ff0af1ee unknown 1 month ago
1398
}
1399
1400
# [ test ]
1401
fn decode_rejects_bad_changes ( ) {
1402
let c = sample_change ( ) ;
1403
let mut v = c.to_value ( ) ;
1404
if let Value:: Map( m) = & mut v {
0f93f49c Andy McCall 1 month ago ↶
1405
m.insert ( 0 , Value:: Uint( 4 ) ) ;
ff0af1ee unknown 1 month ago
1406
}
75167672 Andy McCall 1 month ago
1407
assert! (
1408
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " version" ) )
1409
) ;
ff0af1ee unknown 1 month ago
1410
1411
let mut v = c.to_value ( ) ;
1412
if let Value:: Map( m) = & mut v {
75167672 Andy McCall 1 month ago
1413
m.insert (
1414
2 ,
1415
Value:: Array( vec! [ Value:: Bytes( vec! [ 2 ; 32 ] ) , Value:: Bytes( vec! [ 1 ; 32 ] ) ] ) ,
1416
) ;
ff0af1ee unknown 1 month ago
1417
}
75167672 Andy McCall 1 month ago
1418
assert! (
1419
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " sorted" ) )
1420
) ;
ff0af1ee unknown 1 month ago
1421
1422
let mut v = c.to_value ( ) ;
1423
if let Value:: Map( m) = & mut v {
1424
m.insert ( 9 , Value:: map( [ ( 0 , Value:: Null) ] ) ) ;
1425
}
75167672 Andy McCall 1 month ago
1426
assert! (
1427
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " extra" ) )
1428
) ;
6f8aeb99 Andy McCall 1 month ago ↶
1429
}
1430
01a47cd7 Andy McCall 1 month ago ↶
1431
fn big_lines ( n : usize ) -> Lines {
6f8aeb99 Andy McCall 1 month ago ↶
1432
( 0 .. n)
1433
.map ( | i | format! ( " line {i} of a large insert\n " ) .into_bytes ( ) )
1434
.collect ( )
1435
}
1436
01a47cd7 Andy McCall 1 month ago ↶
1437
fn with_insert ( mut c : Change, lines : Lines) -> Change {
6f8aeb99 Andy McCall 1 month ago ↶
1438
c.edits[ 0 ] = Edit:: Insert {
1439
node: NodeId( [ 7 ; 16 ] ) ,
1440
after: AtomRef:: Start,
1441
before: AtomRef:: End,
1442
lines,
1443
} ;
1444
c
1445
}
1446
1447
# [ test ]
1448
fn large_inserts_are_stored_as_text_blobs ( ) {
1449
assert! (
1450
! sample_change ( ) .has_stored_text ( ) ,
1451
" a small insert stays inline"
1452
) ;
1453
let lines = big_lines ( 500 ) ;
1454
let c = with_insert ( sample_change ( ) , lines.clone ( ) ) ;
1455
assert! ( c.has_stored_text ( ) ) ;
1456
let ( bytes, texts) = c.encode_parts ( ) ;
1457
assert_eq! ( bytes, c.encode ( ) ) ;
1458
assert! ( bytes.len ( ) < 1024 , " {}" , bytes.len ( ) ) ;
1459
assert_eq! ( texts.len ( ) , 1 ) ;
0f93f49c Andy McCall 1 month ago ↶
1460
1461
assert_eq! ( texts[ 0 ] .form, TextForm:: Raw) ;
1462
assert_eq! ( texts[ 0 ] .content, lines.bytes ( ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
1463
assert_eq! ( texts[ 0 ] .blob, Blob:: of( & texts[ 0 ] .content) ) ;
1464
assert! ( Change:: decode( & bytes) .is_err ( ) , " reading it needs the text" ) ;
0f93f49c Andy McCall 1 month ago ↶
1465
let d = Change:: decode_with( & bytes, & mut | r, n, form| {
6f8aeb99 Andy McCall 1 month ago ↶
1466
assert_eq! ( * r, texts[ 0 ] .blob.hash ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1467
assert_eq! ( form, TextForm:: Raw) ;
1468
text_lines ( & texts[ 0 ] .content, n, form)
6f8aeb99 Andy McCall 1 month ago ↶
1469
} )
1470
.unwrap ( ) ;
1471
assert_eq! ( d, c) ;
1472
assert_eq! ( d.hash ( ) , c.hash ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1473
1474
let mut v2 = c.clone ( ) ;
1475
v2.version = 2 ;
1476
let ( b2, t2) = v2.encode_parts ( ) ;
1477
assert_eq! ( t2[ 0 ] .form, TextForm:: Cbor) ;
1478
assert_eq! ( t2[ 0 ] .content, cbor:: encode_byte_strings( & lines) ) ;
1479
let d2 = Change:: decode_with( & b2, & mut | _ , n, form| text_lines ( & t2[ 0 ] .content, n, form) )
1480
.unwrap ( ) ;
1481
assert_eq! ( d2, v2) ;
1482
1483
1484
let odd: Lines = ( 0 .. 500 )
1485
.map ( | i | format! ( " no newline {i} " ) .into_bytes ( ) )
1486
.collect ( ) ;
1487
let c3 = with_insert ( sample_change ( ) , odd.clone ( ) ) ;
1488
let ( b3, t3) = c3.encode_parts ( ) ;
1489
assert_eq! ( t3[ 0 ] .form, TextForm:: CborFallback) ;
1490
let d3 = Change:: decode_with( & b3, & mut | _ , n, form| text_lines ( & t3[ 0 ] .content, n, form) )
1491
.unwrap ( ) ;
1492
assert_eq! ( d3, c3) ;
1493
let marked = cbor:: encode_byte_strings( & lines) ;
1494
assert! ( text_lines ( & marked, 500 , TextForm:: CborFallback) .is_err ( ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
1495
}
1496
1497
# [ test ]
1498
fn version_1_changes_keep_their_inline_inserts_and_hashes ( ) {
1499
let mut c = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1500
c.version = 1 ;
1501
let bytes = c.encode ( ) ;
1502
assert! ( bytes.len ( ) > TEXT_BLOB_MIN ) ;
1503
let ( again, texts) = c.encode_parts ( ) ;
1504
assert! ( texts.is_empty ( ) ) ;
1505
assert_eq! ( again, bytes) ;
1506
let d = Change:: decode( & bytes) .unwrap ( ) ;
1507
assert_eq! ( d.version, 1 ) ;
1508
assert_eq! ( d.encode ( ) , bytes) ;
1509
1510
let mut v = c.to_value ( ) ;
1511
if let Value:: Map( m) = & mut v {
1512
m.insert ( 0 , Value:: Uint( 2 ) ) ;
1513
}
1514
assert! (
1515
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " text blob" ) )
1516
) ;
a4fad5b0 Andy McCall 1 month ago ↶
1517
}
1518
1519
# [ test ]
1520
fn a_skeleton_leaves_every_insert_out_and_says_where_its_lines_are ( ) {
1521
let mut c = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1522
c.edits.push ( Edit:: Insert {
1523
node: NodeId( [ 8 ; 16 ] ) ,
1524
after: AtomRef:: Start,
1525
before: AtomRef:: End,
01a47cd7 Andy McCall 1 month ago ↶
1526
lines: vec! [ b " small\n " .to_vec ( ) , b " insert\n " .to_vec ( ) ] .into ( ) ,
a4fad5b0 Andy McCall 1 month ago ↶
1527
} ) ;
1528
let ( bytes, stored) = c.encode_parts ( ) ;
1529
let me = ObjectRef( [ 4 ; 32 ] ) ;
1530
let ( skel, texts) = Change:: decode_skeleton( & bytes, me) .unwrap ( ) ;
1531
assert_eq! ( texts.len ( ) , 2 ) ;
1532
let mut filled = skel.clone ( ) ;
1533
let mut next = texts.iter ( ) ;
1534
for e in filled.edits.iter_mut ( ) {
1535
if let Edit:: Insert { lines, .. } = e {
1536
assert! ( lines.is_empty ( ) , " no line kept" ) ;
1537
* lines = match next.next ( ) .unwrap ( ) {
0f93f49c Andy McCall 1 month ago ↶
1538
TextRef:: Blob( r, n, form) => {
a4fad5b0 Andy McCall 1 month ago ↶
1539
assert_eq! ( * r, stored[ 0 ] .blob.hash ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1540
text_lines ( & stored[ 0 ] .content, * n, * form) .unwrap ( )
a4fad5b0 Andy McCall 1 month ago ↶
1541
}
1542
TextRef:: Inline { change, start, end } => {
1543
assert_eq! ( * change, me) ;
1544
inline_lines ( & bytes[ * start as usize .. * end as usize ] ) .unwrap ( )
1545
}
1546
} ;
1547
}
1548
}
1549
assert_eq! ( filled, c, " filled in, it is the change" ) ;
1550
1551
let mut v1 = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1552
v1.version = 1 ;
1553
assert! ( Change:: decode_skeleton( & v1.encode ( ) , me) .is_ok ( ) ) ;
1554
let mut v = v1.to_value ( ) ;
1555
if let Value:: Map( m) = & mut v {
1556
m.insert ( 0 , Value:: Uint( 2 ) ) ;
1557
}
1558
assert! ( Change:: decode_skeleton( & cbor:: encode( & v) , me) .is_err ( ) ) ;
4d6c9c1b Andy McCall 1 month ago ↶
1559
}
1560
1561
# [ test ]
1562
fn metadata_reads_without_the_operations ( ) {
1563
let c = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1564
let ( bytes, _ ) = c.encode_parts ( ) ;
1565
let m = Change:: decode_meta( & bytes) .unwrap ( ) ;
1566
assert_eq! ( m, crate :: state:: ChangeMeta:: of( & c) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
1567
}
1568
1569
# [ test ]
1570
fn text_blobs_are_checked ( ) {
1571
let lines = big_lines ( 500 ) ;
1572
let content = cbor:: encode_byte_strings( & lines) ;
1573
assert_eq! ( content.len ( ) , cbor:: byte_strings_len( & lines) ) ;
1574
assert_eq! (
1575
content,
1576
cbor:: encode( & Value:: Array(
01a47cd7 Andy McCall 1 month ago ↶
1577
lines.iter ( ) .map ( | l | Value:: Bytes( l.to_vec ( ) ) ) .collect ( )
6f8aeb99 Andy McCall 1 month ago ↶
1578
) )
0f93f49c Andy McCall 1 month ago ↶
1579
) ;
1580
assert_eq! ( text_lines ( & content, 500 , TextForm:: Cbor) .unwrap ( ) , lines) ;
1581
assert! ( text_lines ( & content, 499 , TextForm:: Cbor) .is_err ( ) ) ;
1582
assert_eq! (
1583
text_lines ( lines.bytes ( ) , 500 , TextForm:: Raw) .unwrap ( ) ,
1584
lines
6f8aeb99 Andy McCall 1 month ago ↶
1585
) ;
0f93f49c Andy McCall 1 month ago ↶
1586
assert! ( text_lines ( lines.bytes ( ) , 501 , TextForm:: Raw) .is_err ( ) ) ;
01a47cd7 Andy McCall 1 month ago ↶
1587
let small = cbor:: encode_byte_strings( & vec! [ b " x\n " .to_vec ( ) ] .into ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1588
assert! (
1589
text_lines ( & small, 1 , TextForm:: Cbor) .is_err ( ) ,
1590
" short enough to be inline"
1591
) ;
1592
assert! (
1593
text_lines ( b " x\n " , 1 , TextForm:: Raw) .is_err ( ) ,
1594
" short enough to be inline"
1595
) ;
ff0af1ee unknown 1 month ago
1596
}
1597
1598
# [ test ]
1599
fn names_are_validated ( ) {
1600
for bad in [ " " , " ." , " .." , " a/b" , " a\0 b" ] {
1601
assert! ( validate_name ( bad) .is_err ( ) , " {bad:?}" ) ;
1602
}
1603
assert! ( validate_name ( " Foo.c" ) .is_ok ( ) ) ;
1604
assert! ( validate_name ( " ..." ) .is_ok ( ) ) ;
1605
}
1606
1607
# [ test ]
1608
fn mode_and_kind_tags ( ) {
75167672 Andy McCall 1 month ago
1609
for m in [
1610
Mode:: TEXT ,
1611
Mode:: OPAQUE ,
1612
Mode {
1613
exec: true ,
1614
opaque: true ,
1615
} ,
1616
] {
ff0af1ee unknown 1 month ago
1617
assert_eq! ( Mode:: from_bits( m.bits ( ) ) .unwrap ( ) , m) ;
1618
}
1619
assert! ( Mode:: from_bits( 4 ) .is_err ( ) ) ;
75167672 Andy McCall 1 month ago
1620
for k in [
1621
NodeKind:: File,
1622
NodeKind:: Dir,
1623
NodeKind:: Symlink,
1624
NodeKind:: Subrepo,
1625
] {
ff0af1ee unknown 1 month ago
1626
assert_eq! ( NodeKind:: from_tag( k.tag ( ) ) .unwrap ( ) , k) ;
1627
}
1628
}
1629
1630
# [ test ]
1631
fn self_references_resolve ( ) {
1632
let me = ObjectRef( [ 8 ; 32 ] ) ;
75167672 Andy McCall 1 month ago
1633
assert_eq! (
1634
AtomRef:: Atom {
1635
change: ObjectRef:: ZERO ,
1636
index: 3
1637
}
1638
.resolve ( me) ,
1639
AtomRef:: Atom {
1640
change: me,
1641
index: 3
1642
}
1643
) ;
ff0af1ee unknown 1 month ago
1644
assert_eq! ( AtomRef:: Start.resolve ( me) , AtomRef:: Start) ;
1645
let other = ObjectRef( [ 1 ; 32 ] ) ;
75167672 Andy McCall 1 month ago
1646
assert_eq! (
1647
OpRef {
1648
change: other,
1649
index: 1
1650
}
1651
.resolve ( me)
1652
.change,
1653
other
1654
) ;
ff0af1ee unknown 1 month ago
1655
}
1656
1657
# [ test ]
86e682c2 Andy McCall 1 month ago ↶
1658
fn state_object_round_trip ( ) {
75167672 Andy McCall 1 month ago
1659
let st = StateObject:: new( vec! [
1660
ObjectRef( [ 2 ; 32 ] ) ,
1661
ObjectRef( [ 1 ; 32 ] ) ,
1662
ObjectRef( [ 2 ; 32 ] ) ,
1663
] ) ;
86e682c2 Andy McCall 1 month ago ↶
1664
assert_eq! ( st.heads, vec! [ ObjectRef( [ 1 ; 32 ] ) , ObjectRef( [ 2 ; 32 ] ) ] ) ;
1665
assert_eq! ( StateObject:: decode( & st.encode ( ) ) .unwrap ( ) , st) ;
1666
assert_eq! ( StateObject:: new( vec! [ ] ) .heads, vec! [ ] ) ;
1667
assert_ne! ( StateObject:: new( vec! [ ] ) .hash ( ) , st.hash ( ) ) ;
1668
}
1669
1670
# [ test ]
ff0af1ee unknown 1 month ago
1671
fn blob_round_trip ( ) {
75167672 Andy McCall 1 month ago
1672
let b = Blob {
1673
length: 10 ,
1674
chunks: vec! [ ObjectRef( [ 1 ; 32 ] ) , ObjectRef( [ 2 ; 32 ] ) ] ,
1675
} ;
ff0af1ee unknown 1 month ago
1676
assert_eq! ( Blob:: decode( & b.encode ( ) ) .unwrap ( ) , b) ;
1677
}
1678
}