52
52
)
53
53
54
54
// ChecksumFlag is a flag on the checksum to ensure it is non-zero.
55
- const ChecksumFlag uint64 = 1 << 63
55
+ const ChecksumFlag Checksum = 1 << 63
56
56
57
57
// internal reader/writer states
58
58
const (
@@ -65,11 +65,11 @@ const (
65
65
// Pos represents the transactional position of a database.
66
66
type Pos struct {
67
67
TXID TXID
68
- PostApplyChecksum uint64
68
+ PostApplyChecksum Checksum
69
69
}
70
70
71
71
// NewPos returns a new instance of Pos.
72
- func NewPos (txID TXID , postApplyChecksum uint64 ) Pos {
72
+ func NewPos (txID TXID , postApplyChecksum Checksum ) Pos {
73
73
return Pos {
74
74
TXID : txID ,
75
75
PostApplyChecksum : postApplyChecksum ,
@@ -87,9 +87,9 @@ func ParsePos(s string) (Pos, error) {
87
87
return Pos {}, err
88
88
}
89
89
90
- checksum , err := strconv . ParseUint (s [17 :], 16 , 64 )
90
+ checksum , err := ParseChecksum (s [17 :])
91
91
if err != nil {
92
- return Pos {}, fmt . Errorf ( "invalid checksum format: %q" , s [ 17 :])
92
+ return Pos {}, err
93
93
}
94
94
95
95
return Pos {
@@ -100,43 +100,14 @@ func ParsePos(s string) (Pos, error) {
100
100
101
101
// String returns a string representation of the position.
102
102
func (p Pos ) String () string {
103
- return fmt .Sprintf ("%s/%016x " , p .TXID , p .PostApplyChecksum )
103
+ return fmt .Sprintf ("%s/%s " , p .TXID , p .PostApplyChecksum )
104
104
}
105
105
106
106
// IsZero returns true if the position is empty.
107
107
func (p Pos ) IsZero () bool {
108
108
return p == (Pos {})
109
109
}
110
110
111
- // Marshal serializes the position into JSON.
112
- func (p Pos ) MarshalJSON () ([]byte , error ) {
113
- var v posJSON
114
- v .TXID = p .TXID .String ()
115
- v .PostApplyChecksum = fmt .Sprintf ("%016x" , p .PostApplyChecksum )
116
- return json .Marshal (v )
117
- }
118
-
119
- // Unmarshal deserializes the position from JSON.
120
- func (p * Pos ) UnmarshalJSON (data []byte ) (err error ) {
121
- var v posJSON
122
- if err := json .Unmarshal (data , & v ); err != nil {
123
- return err
124
- }
125
-
126
- if p .TXID , err = ParseTXID (v .TXID ); err != nil {
127
- return fmt .Errorf ("cannot parse txid: %q" , v .TXID )
128
- }
129
- if p .PostApplyChecksum , err = strconv .ParseUint (v .PostApplyChecksum , 16 , 64 ); err != nil {
130
- return fmt .Errorf ("cannot parse post-apply checksum: %q" , v .PostApplyChecksum )
131
- }
132
- return nil
133
- }
134
-
135
- type posJSON struct {
136
- TXID string `json:"txid"`
137
- PostApplyChecksum string `json:"postApplyChecksum"`
138
- }
139
-
140
111
// PosMismatchError is returned when an LTX file is not contiguous with the current position.
141
112
type PosMismatchError struct {
142
113
Pos Pos `json:"pos"`
@@ -197,6 +168,51 @@ func (t *TXID) UnmarshalJSON(data []byte) (err error) {
197
168
return nil
198
169
}
199
170
171
+ // Checksum represents an LTX checksum.
172
+ type Checksum uint64
173
+
174
+ // ParseChecksum parses a 16-character hex string into a checksum.
175
+ func ParseChecksum (s string ) (Checksum , error ) {
176
+ if len (s ) != 16 {
177
+ return 0 , fmt .Errorf ("invalid formatted checksum length: %q" , s )
178
+ }
179
+ v , err := strconv .ParseUint (s , 16 , 64 )
180
+ if err != nil {
181
+ return 0 , fmt .Errorf ("invalid checksum format: %q" , s )
182
+ }
183
+ return Checksum (v ), nil
184
+ }
185
+
186
+ // String returns c formatted as a fixed-width hex number.
187
+ func (c Checksum ) String () string {
188
+ return fmt .Sprintf ("%016x" , uint64 (c ))
189
+ }
190
+
191
+ func (c Checksum ) MarshalJSON () ([]byte , error ) {
192
+ return []byte (`"` + c .String () + `"` ), nil
193
+ }
194
+
195
+ func (c * Checksum ) UnmarshalJSON (data []byte ) (err error ) {
196
+ var s * string
197
+ if err := json .Unmarshal (data , & s ); err != nil {
198
+ return fmt .Errorf ("cannot unmarshal checksum from JSON value" )
199
+ }
200
+
201
+ // Set to zero if value is nil.
202
+ if s == nil {
203
+ * c = 0
204
+ return nil
205
+ }
206
+
207
+ chksum , err := ParseChecksum (* s )
208
+ if err != nil {
209
+ return fmt .Errorf ("cannot parse checksum from JSON string: %q" , * s )
210
+ }
211
+ * c = Checksum (chksum )
212
+
213
+ return nil
214
+ }
215
+
200
216
// Header flags.
201
217
const (
202
218
HeaderFlagMask = uint32 (0x00000001 )
@@ -206,19 +222,19 @@ const (
206
222
207
223
// Header represents the header frame of an LTX file.
208
224
type Header struct {
209
- Version int // based on magic
210
- Flags uint32 // reserved flags
211
- PageSize uint32 // page size, in bytes
212
- Commit uint32 // db size after transaction, in pages
213
- MinTXID TXID // minimum transaction ID
214
- MaxTXID TXID // maximum transaction ID
215
- Timestamp int64 // milliseconds since unix epoch
216
- PreApplyChecksum uint64 // rolling checksum of database before applying this LTX file
217
- WALOffset int64 // file offset from original WAL; zero if journal
218
- WALSize int64 // size of original WAL segment; zero if journal
219
- WALSalt1 uint32 // header salt-1 from original WAL; zero if journal or compaction
220
- WALSalt2 uint32 // header salt-2 from original WAL; zero if journal or compaction
221
- NodeID uint64 // node id where the LTX file was created, zero if unset
225
+ Version int // based on magic
226
+ Flags uint32 // reserved flags
227
+ PageSize uint32 // page size, in bytes
228
+ Commit uint32 // db size after transaction, in pages
229
+ MinTXID TXID // minimum transaction ID
230
+ MaxTXID TXID // maximum transaction ID
231
+ Timestamp int64 // milliseconds since unix epoch
232
+ PreApplyChecksum Checksum // rolling checksum of database before applying this LTX file
233
+ WALOffset int64 // file offset from original WAL; zero if journal
234
+ WALSize int64 // size of original WAL segment; zero if journal
235
+ WALSalt1 uint32 // header salt-1 from original WAL; zero if journal or compaction
236
+ WALSalt2 uint32 // header salt-2 from original WAL; zero if journal or compaction
237
+ NodeID uint64 // node id where the LTX file was created, zero if unset
222
238
}
223
239
224
240
// IsSnapshot returns true if header represents a complete database snapshot.
@@ -313,7 +329,7 @@ func (h *Header) MarshalBinary() ([]byte, error) {
313
329
binary .BigEndian .PutUint64 (b [16 :], uint64 (h .MinTXID ))
314
330
binary .BigEndian .PutUint64 (b [24 :], uint64 (h .MaxTXID ))
315
331
binary .BigEndian .PutUint64 (b [32 :], uint64 (h .Timestamp ))
316
- binary .BigEndian .PutUint64 (b [40 :], h .PreApplyChecksum )
332
+ binary .BigEndian .PutUint64 (b [40 :], uint64 ( h .PreApplyChecksum ) )
317
333
binary .BigEndian .PutUint64 (b [48 :], uint64 (h .WALOffset ))
318
334
binary .BigEndian .PutUint64 (b [56 :], uint64 (h .WALSize ))
319
335
binary .BigEndian .PutUint32 (b [64 :], h .WALSalt1 )
@@ -334,7 +350,7 @@ func (h *Header) UnmarshalBinary(b []byte) error {
334
350
h .MinTXID = TXID (binary .BigEndian .Uint64 (b [16 :]))
335
351
h .MaxTXID = TXID (binary .BigEndian .Uint64 (b [24 :]))
336
352
h .Timestamp = int64 (binary .BigEndian .Uint64 (b [32 :]))
337
- h .PreApplyChecksum = binary .BigEndian .Uint64 (b [40 :])
353
+ h .PreApplyChecksum = Checksum ( binary .BigEndian .Uint64 (b [40 :]) )
338
354
h .WALOffset = int64 (binary .BigEndian .Uint64 (b [48 :]))
339
355
h .WALSize = int64 (binary .BigEndian .Uint64 (b [56 :]))
340
356
h .WALSalt1 = binary .BigEndian .Uint32 (b [64 :])
@@ -371,8 +387,8 @@ func IsValidHeaderFlags(flags uint32) bool {
371
387
372
388
// Trailer represents the ending frame of an LTX file.
373
389
type Trailer struct {
374
- PostApplyChecksum uint64 // rolling checksum of database after this LTX file is applied
375
- FileChecksum uint64 // crc64 checksum of entire file
390
+ PostApplyChecksum Checksum // rolling checksum of database after this LTX file is applied
391
+ FileChecksum Checksum // crc64 checksum of entire file
376
392
}
377
393
378
394
// Validate returns an error if t is invalid.
@@ -394,8 +410,8 @@ func (t *Trailer) Validate() error {
394
410
// MarshalBinary encodes h to a byte slice.
395
411
func (t * Trailer ) MarshalBinary () ([]byte , error ) {
396
412
b := make ([]byte , TrailerSize )
397
- binary .BigEndian .PutUint64 (b [0 :], t .PostApplyChecksum )
398
- binary .BigEndian .PutUint64 (b [8 :], t .FileChecksum )
413
+ binary .BigEndian .PutUint64 (b [0 :], uint64 ( t .PostApplyChecksum ) )
414
+ binary .BigEndian .PutUint64 (b [8 :], uint64 ( t .FileChecksum ) )
399
415
return b , nil
400
416
}
401
417
@@ -405,8 +421,8 @@ func (t *Trailer) UnmarshalBinary(b []byte) error {
405
421
return io .ErrShortBuffer
406
422
}
407
423
408
- t .PostApplyChecksum = binary .BigEndian .Uint64 (b [0 :])
409
- t .FileChecksum = binary .BigEndian .Uint64 (b [8 :])
424
+ t .PostApplyChecksum = Checksum ( binary .BigEndian .Uint64 (b [0 :]) )
425
+ t .FileChecksum = Checksum ( binary .BigEndian .Uint64 (b [8 :]) )
410
426
return nil
411
427
}
412
428
@@ -464,23 +480,23 @@ func NewHasher() hash.Hash64 {
464
480
}
465
481
466
482
// ChecksumPage returns a CRC64 checksum that combines the page number & page data.
467
- func ChecksumPage (pgno uint32 , data []byte ) uint64 {
483
+ func ChecksumPage (pgno uint32 , data []byte ) Checksum {
468
484
return ChecksumPageWithHasher (NewHasher (), pgno , data )
469
485
}
470
486
471
487
// ChecksumPageWithHasher returns a CRC64 checksum that combines the page number & page data.
472
- func ChecksumPageWithHasher (h hash.Hash64 , pgno uint32 , data []byte ) uint64 {
488
+ func ChecksumPageWithHasher (h hash.Hash64 , pgno uint32 , data []byte ) Checksum {
473
489
h .Reset ()
474
490
_ = binary .Write (h , binary .BigEndian , pgno )
475
491
_ , _ = h .Write (data )
476
- return ChecksumFlag | h .Sum64 ()
492
+ return ChecksumFlag | Checksum ( h .Sum64 () )
477
493
}
478
494
479
495
// ChecksumReader reads an entire database file from r and computes its rolling checksum.
480
- func ChecksumReader (r io.Reader , pageSize int ) (uint64 , error ) {
496
+ func ChecksumReader (r io.Reader , pageSize int ) (Checksum , error ) {
481
497
data := make ([]byte , pageSize )
482
498
483
- var chksum uint64
499
+ var chksum Checksum
484
500
for pgno := uint32 (1 ); ; pgno ++ {
485
501
if _ , err := io .ReadFull (r , data ); err == io .EOF {
486
502
break
0 commit comments