github.com/scottcagno/storage@v1.8.0/pkg/_se/record.go (about) 1 package _se 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "hash/crc32" 7 "io" 8 "unsafe" 9 ) 10 11 const ( 12 headerSize = int(unsafe.Sizeof(header{})) 13 magicIdent = 0xdeadbeef 14 ) 15 16 type header struct { 17 id uint32 // 4 bytes 18 crc uint32 // 4 bytes 19 magic uint32 // 4 bytes 20 pages uint16 // 2 bytes 21 length uint16 // 2 bytes 22 } 23 24 func (h *header) String() string { 25 return fmt.Sprintf("header:\tid=%d, crc=%d, magic=%d, pages=%d, length=%d", 26 h.id, h.crc, h.magic, h.pages, h.length) 27 } 28 29 type record struct { 30 *header 31 data []byte 32 padding int 33 } 34 35 func makeRecord(id uint32, data []byte) *record { 36 alignedSize := align(len(data)+headerSize, pageSize-1) 37 //fmt.Printf("alignedSize=%d, dataLen=%d, padding=%d\n", alignedSize, len(data), (alignedSize - (len(data) + headerSize))) 38 return &record{ 39 header: &header{ 40 id: id, 41 crc: crc32.ChecksumIEEE(data), 42 magic: magicIdent, 43 pages: uint16(alignedSize / pageSize), 44 length: uint16(len(data)), 45 }, 46 data: data, 47 padding: alignedSize - (len(data) + headerSize), 48 } 49 } 50 51 func (r *record) write(ws io.WriteSeeker) (int, error) { 52 var nn int 53 buf := make([]byte, r.header.pages*pageSize) 54 binary.LittleEndian.PutUint32(buf[nn:nn+4], r.header.id) 55 nn += 4 56 binary.LittleEndian.PutUint32(buf[nn:nn+4], r.header.crc) 57 nn += 4 58 binary.LittleEndian.PutUint32(buf[nn:nn+4], r.header.magic) 59 nn += 4 60 binary.LittleEndian.PutUint16(buf[nn:nn+2], r.header.pages) 61 nn += 2 62 binary.LittleEndian.PutUint16(buf[nn:nn+2], r.header.length) 63 nn += 2 64 copy(buf[nn:], r.data) 65 n, err := ws.Write(buf) 66 if err != nil { 67 return nn, err 68 } 69 nn += n 70 if r.padding > 0 { 71 _, err = ws.Write(make([]byte, r.padding)) 72 if err != nil { 73 return nn, err 74 } 75 } 76 nn += r.padding 77 return nn, nil 78 } 79 80 func (r *record) read(rs io.ReadSeeker) (int, error) { 81 var nn int 82 var buf [headerSize]byte 83 n, err := rs.Read(buf[:]) 84 if err != nil { 85 return n, err 86 } 87 h := new(header) 88 h.id = binary.LittleEndian.Uint32(buf[nn : nn+4]) 89 nn += 4 90 h.crc = binary.LittleEndian.Uint32(buf[nn : nn+4]) 91 nn += 4 92 h.magic = binary.LittleEndian.Uint32(buf[nn : nn+4]) 93 nn += 4 94 h.pages = binary.LittleEndian.Uint16(buf[nn : nn+2]) 95 nn += 2 96 h.length = binary.LittleEndian.Uint16(buf[nn : nn+2]) 97 nn += 2 98 r.header = h 99 r.data = make([]byte, r.header.length) 100 n, err = rs.Read(r.data) 101 if err != nil { 102 return nn, err 103 } 104 nn += n 105 if r.padding > 0 { 106 _, err = rs.Seek(int64(r.padding), io.SeekCurrent) 107 if err != nil { 108 return nn, err 109 } 110 } 111 return nn, nil 112 } 113 114 func (r *record) raw() []byte { 115 var n int 116 buf := make([]byte, r.header.pages*pageSize) 117 binary.LittleEndian.PutUint32(buf[n:n+4], r.header.id) 118 n += 4 119 binary.LittleEndian.PutUint32(buf[n:n+4], r.header.crc) 120 n += 4 121 binary.LittleEndian.PutUint32(buf[n:n+4], r.header.magic) 122 n += 4 123 binary.LittleEndian.PutUint16(buf[n:n+2], r.header.pages) 124 n += 2 125 binary.LittleEndian.PutUint16(buf[n:n+2], r.header.length) 126 n += 2 127 n += copy(buf[n:], r.data) 128 return buf 129 } 130 131 func (r *record) String() string { 132 ss := fmt.Sprintf("%s\n", r.header) 133 ss += fmt.Sprintf("record:\tpadding=%d, data=%q", r.padding, r.data) 134 return ss 135 }