github.com/vescale/zgraph@v0.0.0-20230410094002-959c02d50f95/codec/encoder.go (about) 1 // Copyright 2022 zGraph Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package codec 16 17 import ( 18 "encoding/binary" 19 "fmt" 20 "sort" 21 22 "github.com/vescale/zgraph/datum" 23 "github.com/vescale/zgraph/types" 24 ) 25 26 // PropertyEncoder is used to encode datums into value bytes. 27 type PropertyEncoder struct { 28 rowBytes 29 30 values []datum.Datum 31 } 32 33 // Encode encodes properties into a value bytes. 34 func (e *PropertyEncoder) Encode(buf []byte, labelIDs, propertyIDs []uint16, values []datum.Datum) ([]byte, error) { 35 e.reform(labelIDs, propertyIDs, values) 36 for i, value := range e.values { 37 err := e.encodeDatum(value) 38 if err != nil { 39 return nil, err 40 } 41 e.offsets[i] = uint16(len(e.data)) 42 } 43 return e.toBytes(buf[:0]), nil 44 } 45 46 func (e *PropertyEncoder) encodeDatum(value datum.Datum) error { 47 // Put the type information first. 48 e.data = append(e.data, byte(value.Type())) 49 switch value.Type() { 50 case types.Int: 51 e.data = encodeInt(e.data, datum.AsInt(value)) 52 case types.Float: 53 e.data = EncodeFloat(e.data, datum.AsFloat(value)) 54 case types.String: 55 e.data = append(e.data, datum.AsBytes(value)...) 56 case types.Date: 57 e.data = encodeDate(e.data, datum.AsDate(value)) 58 default: 59 return fmt.Errorf("unsupported encode type %T", value) 60 } 61 return nil 62 } 63 64 func encodeInt(buf []byte, iVal int64) []byte { 65 var tmp [8]byte 66 if int64(int8(iVal)) == iVal { 67 buf = append(buf, byte(iVal)) 68 } else if int64(int16(iVal)) == iVal { 69 binary.LittleEndian.PutUint16(tmp[:], uint16(iVal)) 70 buf = append(buf, tmp[:2]...) 71 } else if int64(int32(iVal)) == iVal { 72 binary.LittleEndian.PutUint32(tmp[:], uint32(iVal)) 73 buf = append(buf, tmp[:4]...) 74 } else { 75 binary.LittleEndian.PutUint64(tmp[:], uint64(iVal)) 76 buf = append(buf, tmp[:8]...) 77 } 78 return buf 79 } 80 81 func encodeDate(buf []byte, date *datum.Date) []byte { 82 return encodeInt(buf, int64(date.UnixEpochDays())) 83 } 84 85 func (e *PropertyEncoder) reform(labelIDs, propertyIDs []uint16, values []datum.Datum) { 86 e.labelIDs = append(e.labelIDs[:0], labelIDs...) 87 e.propertyIDs = append(e.propertyIDs[:0], propertyIDs...) 88 e.offsets = make([]uint16, len(e.propertyIDs)) 89 e.data = e.data[:0] 90 e.values = e.values[:0] 91 for i := range values { 92 e.values = append(e.values, values[i]) 93 } 94 95 sort.Slice(e.labelIDs, func(i, j int) bool { 96 return e.labelIDs[i] < e.labelIDs[j] 97 }) 98 sort.Sort(&propertySorter{ 99 propertyIDs: e.propertyIDs, 100 values: e.values, 101 }) 102 } 103 104 type propertySorter struct { 105 propertyIDs []uint16 106 values []datum.Datum 107 } 108 109 // Less implements the Sorter interface. 110 func (ps *propertySorter) Less(i, j int) bool { 111 return ps.propertyIDs[i] < ps.propertyIDs[j] 112 } 113 114 // Len implements the Sorter interface. 115 func (ps *propertySorter) Len() int { 116 return len(ps.propertyIDs) 117 } 118 119 // Swap implements the Sorter interface. 120 func (ps *propertySorter) Swap(i, j int) { 121 ps.propertyIDs[i], ps.propertyIDs[j] = ps.propertyIDs[j], ps.propertyIDs[i] 122 ps.values[i], ps.values[j] = ps.values[j], ps.values[i] 123 }