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  }