github.com/altipla-consulting/ravendb-go-client@v0.1.3/hash_calculator.go (about)

     1  package ravendb
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/md5"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"io"
     9  	"reflect"
    10  	"sort"
    11  	"time"
    12  )
    13  
    14  type HashCalculator struct {
    15  	_buffer bytes.Buffer
    16  }
    17  
    18  func (h *HashCalculator) getHash() string {
    19  	data := h._buffer.Bytes()
    20  	return fmt.Sprintf("%x", md5.Sum(data))
    21  }
    22  
    23  func (h *HashCalculator) write(v interface{}) {
    24  	if v == nil {
    25  		io.WriteString(&h._buffer, "null")
    26  		return
    27  	}
    28  	switch v2 := v.(type) {
    29  	case string:
    30  		io.WriteString(&h._buffer, v2)
    31  	case []string:
    32  		if len(v2) == 0 {
    33  			io.WriteString(&h._buffer, "null-list-str")
    34  			return
    35  		}
    36  		must(binary.Write(&h._buffer, binary.LittleEndian, int64(len(v2))))
    37  		for _, s := range v2 {
    38  			io.WriteString(&h._buffer, s)
    39  		}
    40  	case []interface{}:
    41  		for _, v := range v2 {
    42  			h.write(v)
    43  		}
    44  	case map[string]string:
    45  		if len(v2) == 0 {
    46  			io.WriteString(&h._buffer, "null-dic<string,string>")
    47  			return
    48  		}
    49  		must(binary.Write(&h._buffer, binary.LittleEndian, int64(len(v2))))
    50  		// in Go iteration over map is not stable, so need to manually sort keys
    51  		var keys []string
    52  		for k := range v2 {
    53  			keys = append(keys, k)
    54  		}
    55  		sort.Strings(keys)
    56  		for _, k := range keys {
    57  			v := v2[k]
    58  			io.WriteString(&h._buffer, k)
    59  			io.WriteString(&h._buffer, v)
    60  		}
    61  	case map[string]interface{}:
    62  		// this is Parameters
    63  		if len(v2) == 0 {
    64  			io.WriteString(&h._buffer, "null-dic<string,object>")
    65  			return
    66  		}
    67  		must(binary.Write(&h._buffer, binary.LittleEndian, int64(len(v2))))
    68  		// in Go iteration over map is not stable, so need to manually sort keys
    69  		var keys []string
    70  		for k := range v2 {
    71  			keys = append(keys, k)
    72  		}
    73  		sort.Strings(keys)
    74  		for _, k := range keys {
    75  			v = v2[k]
    76  			io.WriteString(&h._buffer, k)
    77  			if v == nil {
    78  				io.WriteString(&h._buffer, "null")
    79  				continue
    80  			}
    81  			tp := reflect.TypeOf(v)
    82  			if _, ok := isPtrStruct(tp); ok || tp.Kind() == reflect.Struct {
    83  				// when value of parameter is a struct or pointer to struct
    84  				// it could be our param like SuggestionOptions or
    85  				// param that is custom type used by the user
    86  				s := fmt.Sprintf("%#v", v)
    87  				io.WriteString(&h._buffer, s)
    88  				continue
    89  			}
    90  			h.write(v)
    91  		}
    92  	case bool:
    93  		var toWrite int32 = 1
    94  		if v2 {
    95  			toWrite = 2
    96  		}
    97  		must(binary.Write(&h._buffer, binary.LittleEndian, toWrite))
    98  	case time.Time:
    99  		t := v2.UTC().Unix()
   100  		must(binary.Write(&h._buffer, binary.LittleEndian, t))
   101  	case int:
   102  		must(binary.Write(&h._buffer, binary.LittleEndian, int64(v2)))
   103  	default:
   104  		//fmt.Printf("Writing value '%v' of type %T\n", v, v)
   105  		// binary.Write handles all primitive types, except string and int
   106  		must(binary.Write(&h._buffer, binary.LittleEndian, v))
   107  	}
   108  }