@@ -2,6 +2,7 @@ package ltx_test
22
33import (
44 "bytes"
5+ "crypto/rand"
56 "io"
67 "reflect"
78 "testing"
@@ -239,3 +240,149 @@ func TestDecoder_DecodeDatabaseTo(t *testing.T) {
239240 }
240241 })
241242}
243+
244+ func TestDecoder_64KBPageSize (t * testing.T ) {
245+ const pageSize = 65536 // 64KB - maximum SQLite page size
246+
247+ t .Run ("Compressible" , func (t * testing.T ) {
248+ // Test with compressible data (repetitive pattern).
249+ page1Data := bytes .Repeat ([]byte ("A" ), pageSize )
250+ page2Data := bytes .Repeat ([]byte ("B" ), pageSize )
251+
252+ // Calculate correct post-apply checksum.
253+ chksum := ltx .ChecksumFlag
254+ chksum = ltx .ChecksumFlag | (chksum ^ ltx .ChecksumPage (1 , page1Data ))
255+ chksum = ltx .ChecksumFlag | (chksum ^ ltx .ChecksumPage (2 , page2Data ))
256+
257+ var buf bytes.Buffer
258+ enc , err := ltx .NewEncoder (& buf )
259+ if err != nil {
260+ t .Fatal (err )
261+ }
262+ if err := enc .EncodeHeader (ltx.Header {
263+ Version : ltx .Version ,
264+ PageSize : pageSize ,
265+ Commit : 2 ,
266+ MinTXID : 1 ,
267+ MaxTXID : 1 ,
268+ Timestamp : 1000 ,
269+ }); err != nil {
270+ t .Fatal (err )
271+ }
272+ if err := enc .EncodePage (ltx.PageHeader {Pgno : 1 }, page1Data ); err != nil {
273+ t .Fatal (err )
274+ }
275+ if err := enc .EncodePage (ltx.PageHeader {Pgno : 2 }, page2Data ); err != nil {
276+ t .Fatal (err )
277+ }
278+ enc .SetPostApplyChecksum (chksum )
279+ if err := enc .Close (); err != nil {
280+ t .Fatal (err )
281+ }
282+
283+ // Decode and verify.
284+ dec := ltx .NewDecoder (& buf )
285+ if err := dec .DecodeHeader (); err != nil {
286+ t .Fatal (err )
287+ }
288+
289+ var hdr ltx.PageHeader
290+ data := make ([]byte , pageSize )
291+
292+ if err := dec .DecodePage (& hdr , data ); err != nil {
293+ t .Fatal (err )
294+ }
295+ if hdr .Pgno != 1 {
296+ t .Fatalf ("expected pgno 1, got %d" , hdr .Pgno )
297+ }
298+ if ! bytes .Equal (data , page1Data ) {
299+ t .Fatal ("page 1 data mismatch" )
300+ }
301+ // Should be compressed (flag without uncompressed bit).
302+ if hdr .Flags != ltx .PageHeaderFlagCompressedSize {
303+ t .Fatalf ("expected compressed flag only, got 0x%x" , hdr .Flags )
304+ }
305+
306+ if err := dec .DecodePage (& hdr , data ); err != nil {
307+ t .Fatal (err )
308+ }
309+ if hdr .Pgno != 2 {
310+ t .Fatalf ("expected pgno 2, got %d" , hdr .Pgno )
311+ }
312+ if ! bytes .Equal (data , page2Data ) {
313+ t .Fatal ("page 2 data mismatch" )
314+ }
315+
316+ if err := dec .DecodePage (& hdr , data ); err != io .EOF {
317+ t .Fatalf ("expected EOF, got %v" , err )
318+ }
319+ if err := dec .Close (); err != nil {
320+ t .Fatal (err )
321+ }
322+ })
323+
324+ t .Run ("Incompressible" , func (t * testing.T ) {
325+ // Test with incompressible data (truly random bytes).
326+ page1Data := make ([]byte , pageSize )
327+ if _ , err := rand .Read (page1Data ); err != nil {
328+ t .Fatal (err )
329+ }
330+
331+ // Calculate correct post-apply checksum.
332+ chksum := ltx .ChecksumFlag | ltx .ChecksumPage (1 , page1Data )
333+
334+ var buf bytes.Buffer
335+ enc , err := ltx .NewEncoder (& buf )
336+ if err != nil {
337+ t .Fatal (err )
338+ }
339+ if err := enc .EncodeHeader (ltx.Header {
340+ Version : ltx .Version ,
341+ PageSize : pageSize ,
342+ Commit : 1 ,
343+ MinTXID : 1 ,
344+ MaxTXID : 1 ,
345+ Timestamp : 1000 ,
346+ }); err != nil {
347+ t .Fatal (err )
348+ }
349+ if err := enc .EncodePage (ltx.PageHeader {Pgno : 1 }, page1Data ); err != nil {
350+ t .Fatal (err )
351+ }
352+ enc .SetPostApplyChecksum (chksum )
353+ if err := enc .Close (); err != nil {
354+ t .Fatal (err )
355+ }
356+
357+ // Decode and verify.
358+ dec := ltx .NewDecoder (& buf )
359+ if err := dec .DecodeHeader (); err != nil {
360+ t .Fatal (err )
361+ }
362+
363+ var hdr ltx.PageHeader
364+ data := make ([]byte , pageSize )
365+
366+ if err := dec .DecodePage (& hdr , data ); err != nil {
367+ t .Fatal (err )
368+ }
369+ if hdr .Pgno != 1 {
370+ t .Fatalf ("expected pgno 1, got %d" , hdr .Pgno )
371+ }
372+ if ! bytes .Equal (data , page1Data ) {
373+ t .Fatal ("page 1 data mismatch" )
374+ }
375+ // Should be stored uncompressed (random data doesn't compress).
376+ expectedFlags := ltx .PageHeaderFlagCompressedSize | ltx .PageHeaderFlagUncompressed
377+ if hdr .Flags != expectedFlags {
378+ t .Fatalf ("expected uncompressed flags 0x%x, got 0x%x" , expectedFlags , hdr .Flags )
379+ }
380+
381+ if err := dec .DecodePage (& hdr , data ); err != io .EOF {
382+ t .Fatalf ("expected EOF, got %v" , err )
383+ }
384+ if err := dec .Close (); err != nil {
385+ t .Fatal (err )
386+ }
387+ })
388+ }
0 commit comments