diff --git a/base/bag.go b/base/bag.go new file mode 100644 index 0000000..b3498e6 --- /dev/null +++ b/base/bag.go @@ -0,0 +1,62 @@ +package base + +import ( + "fmt" +) + +// BinaryAttributeGroups contain only BinaryAttributes +// Compact each Attribute to a bit for better storage +type BinaryAttributeGroup struct { + FixedAttributeGroup +} + +func (b *BinaryAttributeGroup) RowSize() int { + return (len(b.attributes) + 7) / 8 +} + +// String gets a human-readable view of this group +func (b *BinaryAttributeGroup) String() string { + if len(b.alloc) > 1 { + return fmt.Sprintf("BinaryAttributeGroup(%d attributes\n thread: %d\n size: %d\n)", len(b.attributes), b.threadNo, b.size) + } + return fmt.Sprintf("BinaryAttributeGroup(%d attributes\n thread: %d\n size: %d\n)", len(b.attributes), b.threadNo, b.size) +} + +func (b *BinaryAttributeGroup) getByteOffset(col, row int) int { + return row*b.RowSize() + col/8 +} + +func (b *BinaryAttributeGroup) set(col, row int, val []byte) { + // Resolve the block + curBlock, blockOffset := b.resolveBlock(col, row) + + // If the value is 1, OR it + if val[0] > 0 { + b.alloc[curBlock][blockOffset] |= (1 << (uint(col) % 8)) + } else { + // Otherwise, AND its complement + b.alloc[curBlock][blockOffset] &= ^(1 << (uint(col) % 8)) + } + + row++ + if row > b.maxRow { + b.maxRow = row + } +} + +func (b *BinaryAttributeGroup) resolveBlock(col, row int) (int, int) { + + byteOffset := row*b.RowSize() + (col / 3) + rowSize := b.RowSize() + return b.FixedAttributeGroup.resolveBlockFromByteOffset(byteOffset, rowSize) + +} + +func (b *BinaryAttributeGroup) get(col, row int) []byte { + curBlock, blockOffset := b.resolveBlock(col, row) + if b.alloc[curBlock][blockOffset]&(1<<(uint(col%8))) > 0 { + return []byte{1} + } else { + return []byte{0} + } +} diff --git a/base/bag_test.go b/base/bag_test.go new file mode 100644 index 0000000..f5366ae --- /dev/null +++ b/base/bag_test.go @@ -0,0 +1,136 @@ +package base + +import ( + "fmt" + . "github.com/smartystreets/goconvey/convey" + "math/rand" + "testing" +) + +func TestBAGSimple(t *testing.T) { + + Convey("Given certain bit data", t, func() { + // Generate said bits + bVals := [][]byte{ + []byte{1, 0, 0}, + []byte{0, 1, 0}, + []byte{0, 0, 1}, + } + + // Create a new DenseInstances + inst := NewDenseInstances() + for i := 0; i < 3; i++ { + inst.AddAttribute(NewBinaryAttribute(fmt.Sprintf("%d", i))) + } + + // Get and re-order the attributes + attrSpecsUnordered := ResolveAllAttributes(inst) + attrSpecs := make([]AttributeSpec, 3) + for _, a := range attrSpecsUnordered { + name := a.GetAttribute().GetName() + if name == "0" { + attrSpecs[0] = a + } else if name == "1" { + attrSpecs[1] = a + } else if name == "2" { + attrSpecs[2] = a + } else { + panic(name) + } + } + + inst.Extend(3) + + for row, b := range bVals { + for col, c := range b { + inst.Set(attrSpecs[col], row, []byte{c}) + } + } + + Convey("All the row values should be the right length...", func() { + inst.MapOverRows(attrSpecs, func(row [][]byte, i int) (bool, error) { + for i := range attrSpecs { + So(len(row[i]), ShouldEqual, 1) + } + return true, nil + }) + }) + + Convey("All the values should be the same...", func() { + inst.MapOverRows(attrSpecs, func(row [][]byte, i int) (bool, error) { + for j := range attrSpecs { + So(row[j][0], ShouldEqual, bVals[i][j]) + } + return true, nil + }) + }) + + }) +} + +func TestBAG(t *testing.T) { + Convey("Given randomly generated bit data", t, func() { + // Generate said bits + bVals := make([][]byte, 0) + for i := 0; i < 50; i++ { + b := make([]byte, 3) + for j := 0; j < 3; j++ { + if rand.NormFloat64() >= 0 { + b[j] = byte(1) + } else { + b[j] = byte(0) + } + } + bVals = append(bVals, b) + } + + // Create a new DenseInstances + inst := NewDenseInstances() + for i := 0; i < 3; i++ { + inst.AddAttribute(NewBinaryAttribute(fmt.Sprintf("%d", i))) + } + + // Get and re-order the attributes + attrSpecsUnordered := ResolveAllAttributes(inst) + attrSpecs := make([]AttributeSpec, 3) + for _, a := range attrSpecsUnordered { + name := a.GetAttribute().GetName() + if name == "0" { + attrSpecs[0] = a + } else if name == "1" { + attrSpecs[1] = a + } else if name == "2" { + attrSpecs[2] = a + } else { + panic(name) + } + } + + inst.Extend(50) + + for row, b := range bVals { + for col, c := range b { + inst.Set(attrSpecs[col], row, []byte{c}) + } + } + + Convey("All the row values should be the right length...", func() { + inst.MapOverRows(attrSpecs, func(row [][]byte, i int) (bool, error) { + for i := range attrSpecs { + So(len(row[i]), ShouldEqual, 1) + } + return true, nil + }) + }) + + Convey("All the values should be the same...", func() { + inst.MapOverRows(attrSpecs, func(row [][]byte, i int) (bool, error) { + for j := range attrSpecs { + So(row[j][0], ShouldEqual, bVals[i][j]) + } + return true, nil + }) + }) + + }) +} diff --git a/base/dense.go b/base/dense.go index b886044..5449fa6 100644 --- a/base/dense.go +++ b/base/dense.go @@ -12,8 +12,8 @@ import ( // in a large grid. type DenseInstances struct { storage *edf.EdfFile - pondMap map[string]int - ponds []*Pond + agMap map[string]int + ags []AttributeGroup lock sync.Mutex fixed bool classAttrs map[AttributeSpec]bool @@ -31,7 +31,7 @@ func NewDenseInstances() *DenseInstances { return &DenseInstances{ storage, make(map[string]int), - make([]*Pond, 0), + make([]AttributeGroup, 0), sync.Mutex{}, false, make(map[AttributeSpec]bool), @@ -41,12 +41,15 @@ func NewDenseInstances() *DenseInstances { } // -// Pond functions +// AttributeGroup functions // -// createPond adds a new Pond to this set of Instances +// createAttributeGroup adds a new AttributeGroup to this set of Instances // IMPORTANT: do not call unless you've acquired the lock -func (inst *DenseInstances) createPond(name string, size int) { +func (inst *DenseInstances) createAttributeGroup(name string, size int) { + + var agAdd AttributeGroup + if inst.fixed { panic("Can't add additional Attributes") } @@ -65,7 +68,7 @@ func (inst *DenseInstances) createPond(name string, size int) { } } if ok { - panic("Can't create pond: pond thread already exists") + panic("Can't create AttributeGroup: thread already exists") } // Write the pool's thread into the file @@ -75,28 +78,38 @@ func (inst *DenseInstances) createPond(name string, size int) { panic(fmt.Sprintf("Can't write thread: %s", err)) } - // Create the pond information - pond := new(Pond) - pond.threadNo = thread.GetId() - pond.parent = inst - pond.attributes = make([]Attribute, 0) - pond.size = size - pond.alloc = make([][]byte, 0) - // Store within instances - inst.pondMap[name] = len(inst.ponds) - inst.ponds = append(inst.ponds, pond) + // Create the AttributeGroup information + if size != 0 { + ag := new(FixedAttributeGroup) + ag.threadNo = thread.GetId() + ag.parent = inst + ag.attributes = make([]Attribute, 0) + ag.size = size + ag.alloc = make([][]byte, 0) + agAdd = ag + } else { + ag := new(BinaryAttributeGroup) + ag.threadNo = thread.GetId() + ag.parent = inst + ag.attributes = make([]Attribute, 0) + ag.size = size + ag.alloc = make([][]byte, 0) + agAdd = ag + } + inst.agMap[name] = len(inst.ags) + inst.ags = append(inst.ags, agAdd) } -// CreatePond adds a new Pond to this set of instances -// with a given name. If the size is 0, a bit-pond is added -// if the size of not 0, then the size of each pond attribute +// CreateAttributeGroup adds a new AttributeGroup to this set of instances +// with a given name. If the size is 0, a bit-ag is added +// if the size of not 0, then the size of each ag attribute // is set to that number of bytes. -func (inst *DenseInstances) CreatePond(name string, size int) (err error) { +func (inst *DenseInstances) CreateAttributeGroup(name string, size int) (err error) { defer func() { if r := recover(); r != nil { var ok bool if err, ok = r.(error); !ok { - err = fmt.Errorf("CreatePond: %v (not created)", r) + err = fmt.Errorf("CreateAttributeGroup: %v (not created)", r) } } }() @@ -104,21 +117,21 @@ func (inst *DenseInstances) CreatePond(name string, size int) (err error) { inst.lock.Lock() defer inst.lock.Unlock() - inst.createPond(name, size) + inst.createAttributeGroup(name, size) return nil } -// GetPond returns a reference to a Pond of a given name / -func (inst *DenseInstances) GetPond(name string) (*Pond, error) { +// GetAttributeGroup returns a reference to a AttributeGroup of a given name / +func (inst *DenseInstances) GetAttributeGroup(name string) (AttributeGroup, error) { inst.lock.Lock() defer inst.lock.Unlock() - // Check if the pond exists - if id, ok := inst.pondMap[name]; !ok { - return nil, fmt.Errorf("Pond '%s' doesn't exist", name) + // Check if the ag exists + if id, ok := inst.agMap[name]; !ok { + return nil, fmt.Errorf("AttributeGroup '%s' doesn't exist", name) } else { - // Return the pond - return inst.ponds[id], nil + // Return the ag + return inst.ags[id], nil } } @@ -127,7 +140,7 @@ func (inst *DenseInstances) GetPond(name string) (*Pond, error) { // // AddAttribute adds an Attribute to this set of DenseInstances -// Creates a default Pond for it if a suitable one doesn't exist. +// Creates a default AttributeGroup for it if a suitable one doesn't exist. // Returns an AttributeSpec for subsequent Set() calls. // // IMPORTANT: will panic if storage has been allocated via Extend. @@ -139,48 +152,54 @@ func (inst *DenseInstances) AddAttribute(a Attribute) AttributeSpec { panic("Can't add additional Attributes") } - // Generate a default Pond name - pond := "FLOAT" + // Generate a default AttributeGroup name + ag := "FLOAT" if _, ok := a.(*CategoricalAttribute); ok { - pond = "CAT" + ag = "CAT" } else if _, ok := a.(*FloatAttribute); ok { - pond = "FLOAT" + ag = "FLOAT" + } else if _, ok := a.(*BinaryAttribute); ok { + ag = "BIN" } else { panic("Unrecognised Attribute type") } - // Create the pond if it doesn't exist - if _, ok := inst.pondMap[pond]; !ok { - inst.createPond(pond, 8) + // Create the ag if it doesn't exist + if _, ok := inst.agMap[ag]; !ok { + if ag != "BIN" { + inst.createAttributeGroup(ag, 8) + } else { + inst.createAttributeGroup(ag, 0) + } } - id := inst.pondMap[pond] - p := inst.ponds[id] - p.attributes = append(p.attributes, a) + id := inst.agMap[ag] + p := inst.ags[id] + p.AddAttribute(a) inst.attributes = append(inst.attributes, a) - return AttributeSpec{id, len(p.attributes) - 1, a} + return AttributeSpec{id, len(p.Attributes()) - 1, a} } -// AddAttributeToPond adds an Attribute to a given pond -func (inst *DenseInstances) AddAttributeToPond(newAttribute Attribute, pond string) (AttributeSpec, error) { +// AddAttributeToAttributeGroup adds an Attribute to a given ag +func (inst *DenseInstances) AddAttributeToAttributeGroup(newAttribute Attribute, ag string) (AttributeSpec, error) { inst.lock.Lock() defer inst.lock.Unlock() - // Check if the pond exists - if _, ok := inst.pondMap[pond]; !ok { - return AttributeSpec{-1, 0, nil}, fmt.Errorf("Pond '%s' doesn't exist. Call CreatePond() first", pond) + // Check if the ag exists + if _, ok := inst.agMap[ag]; !ok { + return AttributeSpec{-1, 0, nil}, fmt.Errorf("Pond '%s' doesn't exist. Call CreatePond() first", ag) } - id := inst.pondMap[pond] - p := inst.ponds[id] - for i, a := range p.attributes { + id := inst.agMap[ag] + p := inst.ags[id] + for i, a := range p.Attributes() { if !a.Compatable(newAttribute) { - return AttributeSpec{-1, 0, nil}, fmt.Errorf("Attribute %s is not compatable with %s in pond '%s' (position %d)", newAttribute, a, pond, i) + return AttributeSpec{-1, 0, nil}, fmt.Errorf("Attribute %s is not compatable with %s in pond '%s' (position %d)", newAttribute, a, ag, i) } } - p.attributes = append(p.attributes, newAttribute) + p.AddAttribute(newAttribute) inst.attributes = append(inst.attributes, newAttribute) - return AttributeSpec{id, len(p.attributes) - 1, newAttribute}, nil + return AttributeSpec{id, len(p.Attributes()) - 1, newAttribute}, nil } // GetAttribute returns an Attribute equal to the argument. @@ -192,8 +211,8 @@ func (inst *DenseInstances) GetAttribute(get Attribute) (AttributeSpec, error) { inst.lock.Lock() defer inst.lock.Unlock() - for i, p := range inst.ponds { - for j, a := range p.attributes { + for i, p := range inst.ags { + for j, a := range p.Attributes() { if a.Equals(get) { return AttributeSpec{i, j, a}, nil } @@ -209,8 +228,8 @@ func (inst *DenseInstances) AllAttributes() []Attribute { defer inst.lock.Unlock() ret := make([]Attribute, 0) - for _, p := range inst.ponds { - for _, a := range p.attributes { + for _, p := range inst.ags { + for _, a := range p.Attributes() { ret = append(ret, a) } } @@ -280,10 +299,9 @@ func (inst *DenseInstances) Extend(rows int) error { // Get the size of each page pageSize := inst.storage.GetPageSize() - for pondName := range inst.ponds { - p := inst.ponds[pondName] + for _, p := range inst.ags { - // Compute pond row storage requirements + // Compute ag row storage requirements rowSize := p.RowSize() // How many rows can we store per page? @@ -293,14 +311,14 @@ func (inst *DenseInstances) Extend(rows int) error { pagesNeeded := uint32(math.Ceil(float64(rows) / rowsPerPage)) // Allocate those pages - r, err := inst.storage.AllocPages(pagesNeeded, p.threadNo) + r, err := inst.storage.AllocPages(pagesNeeded, p.getThreadNo()) if err != nil { panic(fmt.Sprintf("Allocation error: %s (rowSize %d, pageSize %d, rowsPerPage %.2f, tried to allocate %d page(s) and extend by %d row(s))", err, rowSize, pageSize, rowsPerPage, pagesNeeded, rows)) } // Resolve and assign those pages byteBlock := inst.storage.ResolveRange(r) for _, block := range byteBlock { - p.alloc = append(p.alloc, block) + p.addStorage(block) } } inst.fixed = true @@ -319,45 +337,31 @@ func (inst *DenseInstances) Extend(rows int) error { // // IMPORTANT: Will panic() if the val is not the right length func (inst *DenseInstances) Set(a AttributeSpec, row int, val []byte) { - inst.ponds[a.pond].set(a.position, row, val) + inst.ags[a.pond].set(a.position, row, val) } // Get gets a particular Attribute (given as an AttributeSpec) on a particular // row. // AttributeSpecs can be obtained using GetAttribute() or AddAttribute() func (inst *DenseInstances) Get(a AttributeSpec, row int) []byte { - return inst.ponds[a.pond].get(a.position, row) + return inst.ags[a.pond].get(a.position, row) } // RowString returns a string representation of a given row. func (inst *DenseInstances) RowString(row int) string { var buffer bytes.Buffer first := true - for name := range inst.ponds { + for _, p := range inst.ags { if first { first = false } else { buffer.WriteString(" ") } - p := inst.ponds[name] p.appendToRowBuf(row, &buffer) } return buffer.String() } -// -// Row handling functions -// - -func (inst *DenseInstances) allocateRowVector(asv []AttributeSpec) [][]byte { - ret := make([][]byte, len(asv)) - for i, as := range asv { - p := inst.ponds[as.pond] - ret[i] = make([]byte, p.size) - } - return ret -} - // MapOverRows passes each row map into a function. // First argument is a list of AttributeSpec in the order // they're needed in for the function. The second is the function @@ -366,7 +370,7 @@ func (inst *DenseInstances) MapOverRows(asv []AttributeSpec, mapFunc func([][]by rowBuf := make([][]byte, len(asv)) for i := 0; i < inst.maxRow; i++ { for j, as := range asv { - p := inst.ponds[as.pond] + p := inst.ags[as.pond] rowBuf[j] = p.get(as.position, i) } ok, err := mapFunc(rowBuf, i) diff --git a/base/fixed.go b/base/fixed.go new file mode 100644 index 0000000..f504495 --- /dev/null +++ b/base/fixed.go @@ -0,0 +1,135 @@ +package base + +import ( + "bytes" + "fmt" +) + +// FixedAttributeGroups contain a particular number of rows of +// a particular number of Attributes, all of a given type. +type FixedAttributeGroup struct { + threadNo uint32 + parent DataGrid + attributes []Attribute + size int + alloc [][]byte + maxRow int +} + +func (f *FixedAttributeGroup) String() string { + if len(f.alloc) > 1 { + return fmt.Sprintf("FixedAttributeGroup(%d attributes\n thread: %d\n size: %d\n)", len(f.attributes), f.threadNo, f.size) + } + return fmt.Sprintf("FixedAttributeGroup(%d attributes\n thread: %d\n size: %d\n %d \n)", len(f.attributes), f.threadNo, f.size, f.alloc[0][0:60]) +} + +// RowSize returns the size of each row in bytes +func (f *FixedAttributeGroup) RowSize() int { + return len(f.attributes) * f.size +} + +// Attributes returns a slice of Attributes in this FixedAttributeGroup +func (f *FixedAttributeGroup) Attributes() []Attribute { + return f.attributes +} + +// AddAttribute adds an attribute to this FixedAttributeGroup +func (f *FixedAttributeGroup) AddAttribute(a Attribute) error { + f.attributes = append(f.attributes, a) + return nil +} + +// getThreadNo returns the ThreadNo assigned to this FixedAttributeGroup +func (f *FixedAttributeGroup) getThreadNo() uint32 { + return f.threadNo +} + +// addStorage appends the given storage reference to this FixedAttributeGroup +func (f *FixedAttributeGroup) addStorage(a []byte) { + f.alloc = append(f.alloc, a) +} + +// Storage returns a slice of FixedAttributeGroupStorageRefs which can +// be used to access the memory in this pond. +func (f *FixedAttributeGroup) Storage() []AttributeGroupStorageRef { + ret := make([]AttributeGroupStorageRef, len(f.alloc)) + rowSize := f.RowSize() + for i, b := range f.alloc { + ret[i] = AttributeGroupStorageRef{b, len(b) / rowSize} + } + return ret +} + +func (f *FixedAttributeGroup) resolveBlock(col int, row int) (int, int) { + + if len(f.alloc) == 0 { + panic("No blocks to resolve") + } + + // Find where in the pond the byte is + byteOffset := row*f.RowSize() + col*f.size + return f.resolveBlockFromByteOffset(byteOffset, f.RowSize()) +} + +func (f *FixedAttributeGroup) resolveBlockFromByteOffset(byteOffset, rowSize int) (int, int) { + curOffset := 0 + curBlock := 0 + blockOffset := 0 + for { + if curBlock >= len(f.alloc) { + panic("Don't have enough blocks to fulfill") + } + + // Rows are not allowed to span blocks + blockAdd := len(f.alloc[curBlock]) + blockAdd -= blockAdd % rowSize + + // Case 1: we need to skip this allocation + if curOffset+blockAdd < byteOffset { + curOffset += blockAdd + curBlock++ + } else { + blockOffset = byteOffset - curOffset + break + } + } + + return curBlock, blockOffset +} + +func (f *FixedAttributeGroup) set(col int, row int, val []byte) { + + // Double-check the length + if len(val) != f.size { + panic(fmt.Sprintf("Tried to call set() with %d bytes, should be %d", len(val), f.size)) + } + + // Find where in the pond the byte is + curBlock, blockOffset := f.resolveBlock(col, row) + + // Copy the value in + copied := copy(f.alloc[curBlock][blockOffset:], val) + if copied != f.size { + panic(fmt.Sprintf("set() terminated by only copying %d bytes into the current block (should be %d). Check EDF allocation", copied, f.size)) + } + + row++ + if row > f.maxRow { + f.maxRow = row + } +} + +func (f *FixedAttributeGroup) get(col int, row int) []byte { + curBlock, blockOffset := f.resolveBlock(col, row) + return f.alloc[curBlock][blockOffset : blockOffset+f.size] +} + +func (f *FixedAttributeGroup) appendToRowBuf(row int, buffer *bytes.Buffer) { + for i, a := range f.attributes { + postfix := " " + if i == len(f.attributes)-1 { + postfix = "" + } + buffer.WriteString(fmt.Sprintf("%s%s", a.GetStringFromSysVal(f.get(i, row)), postfix)) + } +} diff --git a/base/group.go b/base/group.go new file mode 100644 index 0000000..09faed9 --- /dev/null +++ b/base/group.go @@ -0,0 +1,36 @@ +package base + +import ( + "bytes" +) + +// AttributeGroups store related sequences of system values +// in memory for the DenseInstances structure. +type AttributeGroup interface { + // Returns an EDF thread number + getThreadNo() uint32 + addStorage(a []byte) + // Used for printing + appendToRowBuf(row int, buffer *bytes.Buffer) + // Adds a new Attribute + AddAttribute(Attribute) error + // Returns all Attributes + Attributes() []Attribute + // Gets the byte slice at a given column, row offset + get(int, int) []byte + // Stores the byte slice at a given column, row offset + set(int, int, []byte) + // Gets the size of each row in bytes (rounded up) + RowSize() int + // Gets references to underlying memory + Storage() []AttributeGroupStorageRef + // Returns a human-readable summary + String() string +} + +// AttributeGroupStorageRef is a reference to a particular set +// of allocated rows within a FixedAttributeGroup +type AttributeGroupStorageRef struct { + Storage []byte + Rows int +} diff --git a/base/pond.go b/base/pond.go deleted file mode 100644 index 4163ac0..0000000 --- a/base/pond.go +++ /dev/null @@ -1,122 +0,0 @@ -package base - -import ( - "bytes" - "fmt" -) - -// Ponds contain a particular number of rows of -// a particular number of Attributes, all of a given type. -type Pond struct { - threadNo uint32 - parent DataGrid - attributes []Attribute - size int - alloc [][]byte - maxRow int -} - -func (p *Pond) String() string { - if len(p.alloc) > 1 { - return fmt.Sprintf("Pond(%d attributes\n thread: %d\n size: %d\n)", len(p.attributes), p.threadNo, p.size) - } - return fmt.Sprintf("Pond(%d attributes\n thread: %d\n size: %d\n %d \n)", len(p.attributes), p.threadNo, p.size, p.alloc[0][0:60]) -} - -// PondStorageRef is a reference to a particular set -// of allocated rows within a Pond -type PondStorageRef struct { - Storage []byte - Rows int -} - -// RowSize returns the size of each row in bytes -func (p *Pond) RowSize() int { - return len(p.attributes) * p.size -} - -// Attributes returns a slice of Attributes in this Pond -func (p *Pond) Attributes() []Attribute { - return p.attributes -} - -// Storage returns a slice of PondStorageRefs which can -// be used to access the memory in this pond. -func (p *Pond) Storage() []PondStorageRef { - ret := make([]PondStorageRef, len(p.alloc)) - rowSize := p.RowSize() - for i, b := range p.alloc { - ret[i] = PondStorageRef{b, len(b) / rowSize} - } - return ret -} - -func (p *Pond) resolveBlock(col int, row int) (int, int) { - - if len(p.alloc) == 0 { - panic("No blocks to resolve") - } - - // Find where in the pond the byte is - byteOffset := row*p.RowSize() + col*p.size - curOffset := 0 - curBlock := 0 - blockOffset := 0 - for { - if curBlock >= len(p.alloc) { - panic("Don't have enough blocks to fulfill") - } - - // Rows are not allowed to span blocks - blockAdd := len(p.alloc[curBlock]) - blockAdd -= blockAdd % p.RowSize() - - // Case 1: we need to skip this allocation - if curOffset+blockAdd < byteOffset { - curOffset += blockAdd - curBlock++ - } else { - blockOffset = byteOffset - curOffset - break - } - } - - return curBlock, blockOffset -} - -func (p *Pond) set(col int, row int, val []byte) { - - // Double-check the length - if len(val) != p.size { - panic(fmt.Sprintf("Tried to call set() with %d bytes, should be %d", len(val), p.size)) - } - - // Find where in the pond the byte is - curBlock, blockOffset := p.resolveBlock(col, row) - - // Copy the value in - copied := copy(p.alloc[curBlock][blockOffset:], val) - if copied != p.size { - panic(fmt.Sprintf("set() terminated by only copying %d bytes into the current block (should be %d). Check EDF allocation", copied, p.size)) - } - - row++ - if row > p.maxRow { - p.maxRow = row - } -} - -func (p *Pond) get(col int, row int) []byte { - curBlock, blockOffset := p.resolveBlock(col, row) - return p.alloc[curBlock][blockOffset : blockOffset+p.size] -} - -func (p *Pond) appendToRowBuf(row int, buffer *bytes.Buffer) { - for i, a := range p.attributes { - postfix := " " - if i == len(p.attributes)-1 { - postfix = "" - } - buffer.WriteString(fmt.Sprintf("%s%s", a.GetStringFromSysVal(p.get(i, row)), postfix)) - } -}