package mmdbwriter import ( "bytes" "github.com/maxmind/mmdbwriter/mmdbtype" ) type writtenType struct { pointer mmdbtype.Pointer size int64 } type dataWriter struct { *bytes.Buffer dataMap *dataMap offsets map[dataMapKey]writtenType keyWriter *keyWriter usePointers bool } func newDataWriter(dataMap *dataMap, usePointers bool) *dataWriter { return &dataWriter{ Buffer: &bytes.Buffer{}, dataMap: dataMap, offsets: map[dataMapKey]writtenType{}, keyWriter: newKeyWriter(), usePointers: usePointers, } } func (dw *dataWriter) maybeWrite(value *dataMapValue) (int, error) { written, ok := dw.offsets[value.key] if ok { return int(written.pointer), nil } offset := dw.Len() size, err := value.data.WriteTo(dw) if err != nil { return 0, err } written = writtenType{ pointer: mmdbtype.Pointer(offset), size: size, } dw.offsets[value.key] = written return int(written.pointer), nil } func (dw *dataWriter) WriteOrWritePointer(t mmdbtype.DataType) (int64, error) { keyBytes, err := dw.keyWriter.Key(t) if err != nil { return 0, err } var ok bool if dw.usePointers { var written writtenType written, ok = dw.offsets[dataMapKey(keyBytes)] if ok && written.size > written.pointer.WrittenSize() { // Only use a pointer if it would take less space than writing the // type again. return written.pointer.WriteTo(dw) } } // We can't use the pointers[dataMapKey(keyBytes)] optimization to // avoid an allocation below as the backing buffer for key may change when // we call t.WriteTo. That said, this is the less common code path // so it doesn't matter too much. key := dataMapKey(keyBytes) // TODO: A possible optimization here for simple types would be to just // write key to the dataWriter. This won't necessarily work for Map and // Slice though as they may have internal pointers missing from key. // I briefly tested this and didn't see much difference, but it might // be worth exploring more. offset := dw.Len() size, err := t.WriteTo(dw) if err != nil || ok { return size, err } dw.offsets[key] = writtenType{ pointer: mmdbtype.Pointer(offset), size: size, } return size, nil }