Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 24 additions & 32 deletions bsup/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ import (
// which simply produces a larger and still-valid Container.

const (
Version = 28
MaxMetaSize = 100 * 1024 * 1024
MaxFrameSize = 2 * 1024 * 1024 * 1024
MaxSuperFrameSize = 256 * MaxFrameSize
Expand All @@ -80,7 +79,7 @@ type Header interface {
// Structure of ColumnFrame
//
// S U P C (4 byte string)
// Version (2 bytes)
// Version (2 bytes) - 6-bit major and 10-bit minor (see Version)
// Frame size (8 bytes) - entire frame including header to end of data section
// Root ID (4 bytes)
// Fused type size (8 bytes) - 0 if not present
Expand All @@ -91,20 +90,20 @@ type Header interface {
// Fused type value (encoded as raw type value bytes)
// Data section (encoded as raw bytes with size = FrameSize - MetadataSize)
type ColumnHeader struct {
Version uint16 // off 4
FrameSize uint64 // 6
Root uint32 // 14
MetadataSize uint64 // 18
TypedefsSize uint64 // 26
FusedTypeSize uint64 // 34
Version Version // off 4
FrameSize uint64 // 6
Root uint32 // 14
MetadataSize uint64 // 18
TypedefsSize uint64 // 26
FusedTypeSize uint64 // 34
}

const ColumnHeaderSize = 42

func newColumnHeader(root ID, metadataSize, typedefsSize, fusedTypeSize, dataSectionSize uint64) ColumnHeader {
frameSize := ColumnHeaderSize + metadataSize + typedefsSize + fusedTypeSize + dataSectionSize
return ColumnHeader{
Version: Version,
Version: CurrentVersion,
FrameSize: frameSize,
Root: uint32(root),
MetadataSize: metadataSize,
Expand All @@ -120,7 +119,7 @@ func (c *ColumnHeader) Size() uint64 {
func (c ColumnHeader) Serialize() []byte {
var bytes [ColumnHeaderSize]byte
copy(bytes[0:4], []byte(ColumnMagic))
binary.LittleEndian.PutUint16(bytes[4:], c.Version)
c.Version.Serialize(bytes[4:])
binary.LittleEndian.PutUint64(bytes[6:], c.FrameSize)
binary.LittleEndian.PutUint32(bytes[14:], c.Root)
binary.LittleEndian.PutUint64(bytes[18:], c.MetadataSize)
Expand All @@ -130,7 +129,7 @@ func (c ColumnHeader) Serialize() []byte {
}

func (c *ColumnHeader) Deserialize(bytes [ColumnHeaderSize]byte) {
c.Version = binary.LittleEndian.Uint16(bytes[4:])
c.Version.Deserialize(bytes[4:])
c.FrameSize = binary.LittleEndian.Uint64(bytes[6:])
c.Root = binary.LittleEndian.Uint32(bytes[14:])
c.MetadataSize = binary.LittleEndian.Uint64(bytes[18:])
Expand Down Expand Up @@ -174,15 +173,15 @@ func (c ColumnHeader) check() error {
// Structure of RowFrame
//
// S U P R (4 byte string)
// Version (2 bytes)
// Version (2 bytes) - 6-bit major and 10-bit minor (see Version)
// Frame size (8 bytes) - entire frame including header to end of data section
// Fused type size (8 bytes) - 0 if not present
// Typedefs section size (8 bytes)
// Fused type value (variable bytes)
// Typedefs section (variable bytes)
// Values section size = FrameSize - (FusedTypeSize+TypedefsSize)
type RowHeader struct {
Version uint16
Version Version
FrameSize uint64
FusedTypeSize uint64
TypedefsSize uint64
Expand All @@ -194,7 +193,7 @@ const RowHeaderSize = 30
func newRowHeader(oob bool, typedefsSize, fusedTypeSize, dataSectionSize uint64) RowHeader {
frameSize := RowHeaderSize + +typedefsSize + fusedTypeSize + dataSectionSize
return RowHeader{
Version: Version,
Version: CurrentVersion,
FrameSize: frameSize,
TypedefsSize: typedefsSize,
FusedTypeSize: fusedTypeSize,
Expand All @@ -212,15 +211,15 @@ func (r RowHeader) Serialize(ctrl bool) []byte {
} else {
copy(bytes[0:4], []byte(RowMagic))
}
binary.LittleEndian.PutUint16(bytes[4:], r.Version)
r.Version.Serialize(bytes[4:])
binary.LittleEndian.PutUint64(bytes[6:], r.FrameSize)
binary.LittleEndian.PutUint64(bytes[14:], r.FusedTypeSize)
binary.LittleEndian.PutUint64(bytes[22:], r.TypedefsSize)
return bytes[:]
}

func (r *RowHeader) Deserialize(bytes [RowHeaderSize]byte) {
r.Version = binary.LittleEndian.Uint16(bytes[4:])
r.Version.Deserialize(bytes[4:])
r.FrameSize = binary.LittleEndian.Uint64(bytes[6:])
r.FusedTypeSize = binary.LittleEndian.Uint64(bytes[14:])
r.TypedefsSize = binary.LittleEndian.Uint64(bytes[22:])
Expand Down Expand Up @@ -254,18 +253,18 @@ func (r RowHeader) check() error {
// Structure of SuperFrame footer
//
// S U P S (4 byte string)
// Version (2 bytes)
// Version (2 bytes) - 6-bit major and 10-bit minor (see Version)
// Footer size (8 bytes) - total size of footer including SuperFooterPad
// Total size of previous SuperFrame (8 bytes) - excludes this SuperFrame footer
// Fused type value (variable bytes)
// Stats Section (variable bytes)
// Footer size (8 bytes) - same as above for backward traversal
type SuperFooter struct {
Version uint16 // off 4
FooterSize uint64 // off 6
FusedTypeSize uint64 // off 14
StatsSize uint64 // off 22
SuperFrameSize uint64 // off 30
Version Version // off 4
FooterSize uint64 // off 6
FusedTypeSize uint64 // off 14
StatsSize uint64 // off 22
SuperFrameSize uint64 // off 30
}

const (
Expand All @@ -275,7 +274,7 @@ const (

func newSuperFooter(superFrameSize, fusedTypeSize, statsSize uint64) SuperFooter {
return SuperFooter{
Version: Version,
Version: CurrentVersion,
FooterSize: SuperFooterSize + SuperFooterPad + fusedTypeSize + statsSize,
FusedTypeSize: fusedTypeSize,
StatsSize: statsSize,
Expand All @@ -300,7 +299,7 @@ func (s SuperFooter) StatsSection() (int64, int64) {
func (s SuperFooter) Serialize() []byte {
var bytes [SuperFooterSize]byte
copy(bytes[0:4], []byte(SuperMagic))
binary.LittleEndian.PutUint16(bytes[4:], s.Version)
s.Version.Serialize(bytes[4:])
binary.LittleEndian.PutUint64(bytes[6:], s.FooterSize)
binary.LittleEndian.PutUint64(bytes[14:], s.FusedTypeSize)
binary.LittleEndian.PutUint64(bytes[22:], s.StatsSize)
Expand All @@ -309,7 +308,7 @@ func (s SuperFooter) Serialize() []byte {
}

func (s *SuperFooter) Deserialize(bytes [SuperFooterSize]byte) {
s.Version = binary.LittleEndian.Uint16(bytes[4:])
s.Version.Deserialize(bytes[4:])
s.FooterSize = binary.LittleEndian.Uint64(bytes[6:])
s.FusedTypeSize = binary.LittleEndian.Uint64(bytes[14:])
s.StatsSize = binary.LittleEndian.Uint64(bytes[22:])
Expand Down Expand Up @@ -455,13 +454,6 @@ func checkEOF(err error) error {
return err
}

func checkVersion(version uint16) error {
if version != Version {
return fmt.Errorf("version mismatch: expected %d, found %d", Version, version)
}
return nil
}

func checkSize(msg string, size, max int) error {
// We check for underflow of size since they are stored as unsigned.
if size > max || size < 0 {
Expand Down
64 changes: 64 additions & 0 deletions bsup/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package bsup

import (
"encoding/binary"
"fmt"
)

// Versioning is done with major/minor versions. Any implementation can read
// files with the same major version, forward and backward, but an older minor
// version might not be able to take advantage of or understand new types
// supported by a later minor version under the same major version. No
// compatibility (except possibly backward compatibility depending on the
// implementation) is available between different major versions. Major
// version 0 is for development and there is no compatibility between minor
// versions under major version 0. No releases should be made with a zero
// major version.
//
// A version is encoded in two bytes as a little-endian uint16 with the major
// version in the upper 6 bits and the minor version in the lower 10 bits, so
// a development version encoded before major and minor versions were
// introduced (when the version was a plain uint16) decodes as major version 0.
//
// Last dev minor version: 28
var CurrentVersion = Version{0, 28}

const (
VersionSize = 2

majorBits = 6
minorBits = 10
MaxMajor = 1<<majorBits - 1
MaxMinor = 1<<minorBits - 1
)

type Version struct {
Major uint8
Minor uint16
}

// Serialize encodes v into the first VersionSize bytes of b.
func (v Version) Serialize(b []byte) {
if v.Major > MaxMajor || v.Minor > MaxMinor {
panic(fmt.Sprintf("BSUP version %s out of range", v))
}
binary.LittleEndian.PutUint16(b, uint16(v.Major)<<minorBits|v.Minor)
}

// Deserialize decodes v from the first VersionSize bytes of b.
func (v *Version) Deserialize(b []byte) {
u := binary.LittleEndian.Uint16(b)
v.Major = uint8(u >> minorBits)
v.Minor = u & MaxMinor
}

func (v Version) String() string {
return fmt.Sprintf("%d.%d", v.Major, v.Minor)
}

func checkVersion(version Version) error {
if version.Major != CurrentVersion.Major || (CurrentVersion.Major == 0 && version.Minor != CurrentVersion.Minor) {
return fmt.Errorf("version mismatch: expected %s, found %s", CurrentVersion, version)
}
return nil
}
9 changes: 5 additions & 4 deletions bsup/ztests/const.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ inputs:
outputs:
- name: stdout
data: |
type ColumnHeader={Version:uint16,FrameSize:uint64,Root:uint32,MetadataSize:uint64,TypedefsSize:uint64,FusedTypeSize:uint64}
{Version:28,FrameSize:205,Root:0,MetadataSize:162,TypedefsSize:0,FusedTypeSize:1}::ColumnHeader
type Version={Major:uint8,Minor:uint16}
type ColumnHeader={Version:Version,FrameSize:uint64,Root:uint32,MetadataSize:uint64,TypedefsSize:uint64,FusedTypeSize:uint64}
{Version:{Major:0,Minor:28},FrameSize:205,Root:0,MetadataSize:162,TypedefsSize:0,FusedTypeSize:1}::ColumnHeader
type Const={Value:any,Count:uint32}
{Value:1::any,Count:3}::Const
{Kind:"ColumnFrameFusedType",Type:<int64>}
type SuperFooter={Version:uint16,FooterSize:uint64,FusedTypeSize:uint64,StatsSize:uint64,SuperFrameSize:uint64}
{Version:28,FooterSize:68,FusedTypeSize:1,StatsSize:21,SuperFrameSize:205}::SuperFooter
type SuperFooter={Version:Version,FooterSize:uint64,FusedTypeSize:uint64,StatsSize:uint64,SuperFrameSize:uint64}
{Version:{Major:0,Minor:28},FooterSize:68,FusedTypeSize:1,StatsSize:21,SuperFrameSize:205}::SuperFooter
{Kind:"SuperFrameFusedType",Type:<int64>}
Loading