├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── char └── char.go ├── datetimeoffset └── datetimeoffset.go ├── deserialize.go ├── deserialize_delay.go ├── read.go ├── serialize.go ├── write.go ├── zf_delay_test.go └── zf_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Editor 27 | *.iml 28 | .idea 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - "1.10.x" 5 | - "1.11.x" 6 | - tip 7 | before_install: 8 | - go get github.com/axw/gocov/gocov 9 | - go get github.com/mattn/goveralls 10 | - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi 11 | script: 12 | - $GOPATH/bin/goveralls -service=travis-ci -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 shamaton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zeroformatter 2 | 3 | [![GoDoc](https://godoc.org/github.com/shamaton/zeroformatter?status.svg)](https://godoc.org/github.com/shamaton/zeroformatter) 4 | [![Build Status](https://travis-ci.org/shamaton/zeroformatter.svg?branch=master)](https://travis-ci.org/shamaton/zeroformatter) 5 | [![Coverage Status](https://coveralls.io/repos/github/shamaton/zeroformatter/badge.svg)](https://coveralls.io/github/shamaton/zeroformatter) 6 | [![Releases](https://img.shields.io/github/release/shamaton/zeroformatter.svg)](https://github.com/shamaton/zeroformatter/releases) 7 | 8 | golang version [zeroformatter](https://github.com/neuecc/ZeroFormatter) 9 | 10 | ## Usage 11 | ### Installation 12 | ```sh 13 | go get github.com/shamaton/zeroformatter 14 | ``` 15 | 16 | ### How to use 17 | #### use simply 18 | ```go 19 | package main; 20 | 21 | import ( 22 | "github.com/shamaton/zeroformatter" 23 | "log" 24 | ) 25 | 26 | func main() { 27 | type Struct struct { 28 | String string 29 | } 30 | h := Struct{String: "zeroformatter"} 31 | 32 | d, err := zeroformatter.Serialize(h) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | r := Struct{} 37 | err = zeroformatter.Deserialize(&r, d) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | } 42 | ``` 43 | 44 | #### delay 45 | ```go 46 | package main; 47 | 48 | import ( 49 | "github.com/shamaton/zeroformatter" 50 | "log" 51 | ) 52 | 53 | func how_to_use(b []byte) { 54 | type Struct struct { 55 | String string 56 | } 57 | 58 | r := Struct{} 59 | dds, _ := zeroformatter.DelayDeserialize(&r, b) 60 | 61 | // by element 62 | if err := dds.DeserializeByElement(&r.String); err != nil { 63 | log.Fatal(err) 64 | } 65 | 66 | // or by index 67 | if err := dds.DeserializeByIndex(0); err != nil { 68 | log.Fatal(err) 69 | } 70 | } 71 | ``` 72 | 73 | ## Supported type 74 | 75 | ### Primitive 76 | | C# | Go | 77 | | ---- | ---- | 78 | | Int16 | int16 | 79 | | Int32 | int32, int | 80 | | Int64 | int64 | 81 | | UInt16 | uint16 | 82 | | UInt32 | uint32, uint | 83 | | UInt64 | uint64 | 84 | | Single | float32 | 85 | | Double | float64 | 86 | | Boolean | bool | 87 | | Byte | uint8 | 88 | | SByte | int8 | 89 | | TimeSpan | time.Duration | 90 | | DateTime | time.Time | 91 | | String | string | 92 | 93 | ### Extension within golang 94 | As these types can not convert with primitive type, I defined parent classes in golang. 95 | These are only wrapping. please see codes. 96 | 97 | | C# | Go | 98 | | ---- | ---- | 99 | | Char | zeroformatter.Char(rune) | 100 | | DateTimeOffset | zeroformatter.DateTimeOffset(time.Time) | 101 | 102 | ### Array/Slice 103 | 104 | | C# | Go | 105 | | ---- | ---- | 106 | | T[], List | []T, [N]T | 107 | 108 | ### Map 109 | 110 | | C# | Go | 111 | | ---- | ---- | 112 | | Dictionary | map[K]V | 113 | 114 | ### Object 115 | 116 | | C# | Go | 117 | | ---- | ---- | 118 | | Struct | struct | 119 | 120 | ## Not supported 121 | 122 | `type?` is not supported, because golang doen't allow null in primitve types. 123 | 124 | ## License 125 | 126 | This library is under the MIT License. 127 | -------------------------------------------------------------------------------- /char/char.go: -------------------------------------------------------------------------------- 1 | package char 2 | 3 | // todo : write why this type should exist. 4 | 5 | type Char rune 6 | -------------------------------------------------------------------------------- /datetimeoffset/datetimeoffset.go: -------------------------------------------------------------------------------- 1 | package datetimeoffset 2 | 3 | import "time" 4 | 5 | type DateTimeOffset struct { 6 | time.Time 7 | } 8 | 9 | func Unix(sec int64, nsec int64) DateTimeOffset { 10 | return DateTimeOffset{ 11 | time.Unix(sec, nsec), 12 | } 13 | } 14 | 15 | func Now() DateTimeOffset { 16 | n := time.Now() 17 | return DateTimeOffset{ 18 | time.Unix(n.Unix(), int64(n.Nanosecond())), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /deserialize.go: -------------------------------------------------------------------------------- 1 | package zeroformatter 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "math" 8 | "reflect" 9 | "time" 10 | "unicode/utf16" 11 | "unsafe" 12 | 13 | "github.com/shamaton/zeroformatter/char" 14 | "github.com/shamaton/zeroformatter/datetimeoffset" 15 | ) 16 | 17 | type deserializer struct { 18 | data []byte 19 | } 20 | 21 | const minStructDataSize = 9 22 | 23 | func createDeserializer(data []byte) *deserializer { 24 | return &deserializer{ 25 | data: data, 26 | } 27 | } 28 | 29 | // Deserialize analyzes byte data and set into holder. 30 | func Deserialize(holder interface{}, data []byte) error { 31 | ds := createDeserializer(data) 32 | 33 | t := reflect.ValueOf(holder) 34 | if t.Kind() != reflect.Ptr { 35 | return fmt.Errorf("holder must set pointer value. but got: %t", holder) 36 | } 37 | 38 | t = t.Elem() 39 | if t.Kind() == reflect.Ptr { 40 | t = t.Elem() 41 | } 42 | 43 | // byte to Struct 44 | if t.Kind() == reflect.Struct && !isDateTime(t) && !isDateTimeOffset(t) { 45 | return ds.deserializeStruct(t) 46 | } 47 | 48 | // byte to primitive 49 | _, err := ds.deserialize(t, 0) 50 | return err 51 | } 52 | 53 | func (d *deserializer) deserializeStruct(t reflect.Value) error { 54 | dataLen := len(d.data) 55 | if dataLen < minStructDataSize { 56 | return fmt.Errorf("data size is not enough: %d", dataLen) 57 | } 58 | 59 | // data lookup 60 | offset := uint32(0) 61 | 62 | // size 63 | b, offset := d.readSize4(offset) 64 | size := binary.LittleEndian.Uint32(b) 65 | if size != uint32(dataLen) { 66 | return fmt.Errorf("data size is wrong [ %d : %d ]", size, dataLen) 67 | } 68 | 69 | // index 70 | b, offset = d.readSize4(offset) 71 | dataIndex := binary.LittleEndian.Uint32(b) 72 | numField := t.NumField() 73 | if dataIndex != uint32(numField-1) { 74 | return fmt.Errorf("data index is diffrent [ %d : %d ]", dataIndex, numField-1) 75 | } 76 | 77 | for i := 0; i < numField; i++ { 78 | b, offset = d.readSize4(offset) 79 | dataOffset := binary.LittleEndian.Uint32(b) 80 | if _, err := d.deserialize(t.Field(i), dataOffset); err != nil { 81 | return err 82 | } 83 | } 84 | return nil 85 | } 86 | 87 | func isDateTime(value reflect.Value) bool { 88 | i := value.Interface() 89 | switch i.(type) { 90 | case time.Time: 91 | return true 92 | } 93 | return false 94 | } 95 | 96 | func isDateTimeOffset(value reflect.Value) bool { 97 | i := value.Interface() 98 | switch i.(type) { 99 | case datetimeoffset.DateTimeOffset: 100 | return true 101 | } 102 | return false 103 | } 104 | 105 | func isDuration(value reflect.Value) bool { 106 | // check type 107 | i := value.Interface() 108 | switch i.(type) { 109 | case time.Duration: 110 | return true 111 | } 112 | return false 113 | } 114 | 115 | func isChar(value reflect.Value) bool { 116 | i := value.Interface() 117 | switch i.(type) { 118 | case char.Char: 119 | return true 120 | } 121 | return false 122 | } 123 | 124 | func (d *deserializer) deserialize(rv reflect.Value, offset uint32) (uint32, error) { 125 | var err error 126 | 127 | switch rv.Kind() { 128 | case reflect.Int8: 129 | b, o := d.readSize1(offset) 130 | rv.SetInt(int64(b)) 131 | // update 132 | offset = o 133 | 134 | case reflect.Int16: 135 | // Int16 [short(2)] 136 | b, o := d.readSize2(offset) 137 | _v := binary.LittleEndian.Uint16(b) 138 | rv.SetInt(int64(_v)) 139 | // update 140 | offset = o 141 | 142 | case reflect.Int32: 143 | // char is used instead of rune 144 | if isChar(rv) { 145 | // rune [ushort(2)] 146 | b, o := d.readSize2(offset) 147 | u16s := []uint16{binary.LittleEndian.Uint16(b)} 148 | _v := utf16.Decode(u16s) 149 | v := char.Char(_v[0]) 150 | rv.Set(reflect.ValueOf(v)) 151 | 152 | // update 153 | offset = o 154 | } else { 155 | // Int32 [int(4)] 156 | b, o := d.readSize4(offset) 157 | _v := binary.LittleEndian.Uint32(b) 158 | // NOTE : double cast 159 | rv.SetInt(int64(int32(_v))) 160 | // update 161 | offset = o 162 | } 163 | 164 | case reflect.Int: 165 | // Int32 [int(4)] 166 | b, o := d.readSize4(offset) 167 | _v := binary.LittleEndian.Uint32(b) 168 | // NOTE : double cast 169 | rv.SetInt(int64(int32(_v))) 170 | // update 171 | offset = o 172 | 173 | case reflect.Int64: 174 | if isDuration(rv) { 175 | // todo : NOTE procedure is as same as datetime 176 | b, o1 := d.readSize8(offset) 177 | seconds := binary.LittleEndian.Uint64(b) 178 | b, o2 := d.readSize4(o1) 179 | nanos := binary.LittleEndian.Uint32(b) 180 | v := time.Duration(int64(seconds)*1000*1000 + int64(nanos)) 181 | 182 | rv.Set(reflect.ValueOf(v)) 183 | // update 184 | offset = o2 185 | } else { 186 | // Int64 [long(8)] 187 | b, o := d.readSize8(offset) 188 | v := binary.LittleEndian.Uint64(b) 189 | rv.SetInt(int64(v)) 190 | // update 191 | offset = o 192 | } 193 | 194 | case reflect.Uint8: 195 | // byte in cSharp 196 | _v, o := d.readSize1(offset) 197 | rv.SetUint(uint64(_v)) 198 | // update 199 | offset = o 200 | 201 | case reflect.Uint16: 202 | // Uint16 / Char 203 | b, o := d.readSize2(offset) 204 | v := binary.LittleEndian.Uint16(b) 205 | rv.SetUint(uint64(v)) 206 | // update 207 | offset = o 208 | 209 | case reflect.Uint32: 210 | b, o := d.readSize4(offset) 211 | v := binary.LittleEndian.Uint32(b) 212 | rv.SetUint(uint64(v)) 213 | // update 214 | offset = o 215 | 216 | case reflect.Uint: 217 | b, o := d.readSize4(offset) 218 | v := binary.LittleEndian.Uint32(b) 219 | rv.SetUint(uint64(v)) 220 | // update 221 | offset = o 222 | 223 | case reflect.Uint64: 224 | b, o := d.readSize8(offset) 225 | v := binary.LittleEndian.Uint64(b) 226 | rv.SetUint(v) 227 | // update 228 | offset = o 229 | 230 | case reflect.Float32: 231 | // Single 232 | b, o := d.readSize4(offset) 233 | _v := binary.LittleEndian.Uint32(b) 234 | v := math.Float32frombits(_v) 235 | rv.SetFloat(float64(v)) 236 | // update 237 | offset = o 238 | 239 | case reflect.Float64: 240 | // Double 241 | b, o := d.readSize8(offset) 242 | _v := binary.LittleEndian.Uint64(b) 243 | v := math.Float64frombits(_v) 244 | rv.SetFloat(v) 245 | // update 246 | offset = o 247 | 248 | case reflect.Bool: 249 | b, o := d.readSize1(offset) 250 | if b == 0x01 { 251 | rv.SetBool(true) 252 | } else if b == 0x00 { 253 | rv.SetBool(false) 254 | } 255 | // update 256 | offset = o 257 | 258 | case reflect.String: 259 | b, o := d.readSize4(offset) 260 | l := binary.LittleEndian.Uint32(b) 261 | dd := d.data[o : o+l] 262 | v := *(*string)(unsafe.Pointer(&dd)) 263 | rv.SetString(v) 264 | // update 265 | offset = o + l 266 | 267 | case reflect.Struct: 268 | if isDateTimeOffset(rv) { 269 | b, o1 := d.readSize8(offset) 270 | seconds := binary.LittleEndian.Uint64(b) 271 | b, o2 := d.readSize4(o1) 272 | nanos := binary.LittleEndian.Uint32(b) 273 | b, o3 := d.readSize2(o2) 274 | offMin := binary.LittleEndian.Uint16(b) 275 | 276 | v := datetimeoffset.Unix(int64(seconds)-int64(offMin*60), int64(nanos)) 277 | rv.Set(reflect.ValueOf(v)) 278 | // update 279 | offset = o3 280 | 281 | } else if isDateTime(rv) { 282 | b, o1 := d.readSize8(offset) 283 | seconds := binary.LittleEndian.Uint64(b) 284 | b, o2 := d.readSize4(o1) 285 | nanos := binary.LittleEndian.Uint32(b) 286 | v := time.Unix(int64(seconds), int64(nanos)) 287 | 288 | rv.Set(reflect.ValueOf(v)) 289 | // update 290 | offset = o2 291 | } else { 292 | for i := 0; i < rv.NumField(); i++ { 293 | offset, err = d.deserialize(rv.Field(i), offset) 294 | if err != nil { 295 | return 0, err 296 | } 297 | } 298 | } 299 | 300 | case reflect.Slice: 301 | 302 | // length 303 | b, o := d.readSize4(offset) 304 | l := int(int32(binary.LittleEndian.Uint32(b))) 305 | 306 | // data is null 307 | if l < 0 { 308 | return o, nil 309 | } 310 | 311 | tmpSlice := reflect.MakeSlice(rv.Type(), l, l) 312 | 313 | for i := 0; i < l; i++ { 314 | v := tmpSlice.Index(i) 315 | o, err = d.deserialize(v, o) 316 | if err != nil { 317 | return 0, err 318 | } 319 | } 320 | rv.Set(tmpSlice) 321 | 322 | // update 323 | offset = o 324 | 325 | case reflect.Array: 326 | // element type 327 | e := rv.Type().Elem() 328 | 329 | // length 330 | b, o := d.readSize4(offset) 331 | l := int(int32(binary.LittleEndian.Uint32(b))) 332 | 333 | // data is null 334 | if l < 0 { 335 | return o, nil 336 | } 337 | if l != rv.Len() { 338 | return 0, fmt.Errorf("Array Length is different : data[%d] array[%d]", l, rv.Len()) 339 | } 340 | 341 | for i := 0; i < l; i++ { 342 | v := reflect.New(e).Elem() 343 | o, err = d.deserialize(v, o) 344 | if err != nil { 345 | return 0, err 346 | } 347 | rv.Index(i).Set(v) 348 | } 349 | 350 | // update 351 | offset = o 352 | 353 | case reflect.Map: 354 | key := rv.Type().Key() 355 | value := rv.Type().Elem() 356 | 357 | // map length 358 | b, o := d.readSize4(offset) 359 | l := int(binary.LittleEndian.Uint32(b)) 360 | 361 | if rv.IsNil() { 362 | rv.Set(reflect.MakeMapWithSize(rv.Type(), l)) 363 | } 364 | 365 | for i := 0; i < l; i++ { 366 | k := reflect.New(key).Elem() 367 | v := reflect.New(value).Elem() 368 | o, err = d.deserialize(k, o) 369 | if err != nil { 370 | return 0, err 371 | } 372 | o, err = d.deserialize(v, o) 373 | if err != nil { 374 | return 0, err 375 | } 376 | 377 | rv.SetMapIndex(k, v) 378 | offset = o 379 | } 380 | 381 | case reflect.Ptr: 382 | e := rv.Type().Elem() 383 | v := reflect.New(e).Elem() 384 | offset, err = d.deserialize(v, offset) 385 | rv.Set(v.Addr()) 386 | 387 | default: 388 | err = errors.New(fmt.Sprint("this type is not supported : ", rv.Type())) 389 | } 390 | 391 | return offset, err 392 | } 393 | -------------------------------------------------------------------------------- /deserialize_delay.go: -------------------------------------------------------------------------------- 1 | package zeroformatter 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | type delayDeserializer struct { 10 | *deserializer 11 | holder reflect.Value 12 | processedMap map[uintptr]int 13 | indexArray []uintptr 14 | } 15 | 16 | func createDelayDeserialize(deserializer *deserializer, holder reflect.Value, num int) *delayDeserializer { 17 | return &delayDeserializer{ 18 | deserializer: deserializer, 19 | holder: holder, 20 | processedMap: map[uintptr]int{}, 21 | indexArray: make([]uintptr, num), 22 | } 23 | } 24 | 25 | // DelayDeserialize can delay execution processes which analayze byte data and set into holder. 26 | // If you do not want to deserialize at once, please use this. 27 | func DelayDeserialize(holder interface{}, data []byte) (*delayDeserializer, error) { 28 | 29 | t := reflect.ValueOf(holder) 30 | if t.Kind() != reflect.Ptr { 31 | return nil, fmt.Errorf("holder must set pointer value. but got: %t", holder) 32 | } 33 | 34 | t = t.Elem() 35 | if t.Kind() == reflect.Ptr { 36 | t = t.Elem() 37 | } 38 | 39 | // delaying enable is struct only 40 | if t.Kind() != reflect.Struct || isDateTime(t) || isDateTimeOffset(t) { 41 | return nil, fmt.Errorf("only defined struct can delay deserialize: %t", holder) 42 | } 43 | 44 | // check before detail checking 45 | dataLen := len(data) 46 | if dataLen < minStructDataSize { 47 | return nil, fmt.Errorf("data size is not enough: %d", dataLen) 48 | } 49 | 50 | // create deserializer 51 | ds := createDeserializer(data) 52 | 53 | // check size 54 | offset := uint32(0) 55 | b, offset := ds.readSize4(offset) 56 | 57 | size := binary.LittleEndian.Uint32(b) 58 | if size != uint32(dataLen) { 59 | return nil, fmt.Errorf("data size is wrong [ %d : %d ]", size, dataLen) 60 | } 61 | 62 | // check index 63 | b, offset = ds.readSize4(offset) 64 | dataIndex := binary.LittleEndian.Uint32(b) 65 | numField := t.NumField() 66 | if dataIndex != uint32(numField-1) { 67 | return nil, fmt.Errorf("data index is diffrent [ %d : %d ]", dataIndex, numField-1) 68 | } 69 | 70 | // create delay deserializer 71 | dds := createDelayDeserialize(ds, t, numField) 72 | 73 | // make access info 74 | for i := 0; i < numField; i++ { 75 | e := t.Field(i) 76 | p := e.Addr().Pointer() 77 | dds.processedMap[p] = i 78 | dds.indexArray[i] = p 79 | } 80 | 81 | return dds, nil 82 | } 83 | 84 | func (d *delayDeserializer) DeserializeByIndex(i int, indexes ...int) error { 85 | // index 86 | if err := d.deserializeByIndex(i); err != nil { 87 | return err 88 | } 89 | 90 | // indexes 91 | for _, idx := range indexes { 92 | if err := d.deserializeByIndex(idx); err != nil { 93 | return err 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | func (d *delayDeserializer) deserializeByIndex(i int) error { 100 | if i >= len(d.indexArray) { 101 | return fmt.Errorf("this index is out of range : %d", i) 102 | } 103 | 104 | addr := d.indexArray[i] 105 | return d.deserializeByAddress(addr) 106 | } 107 | 108 | func (d *delayDeserializer) DeserializeByElement(element interface{}, elements ...interface{}) error { 109 | // element 110 | if err := d.deserializeByElement(element); err != nil { 111 | return err 112 | } 113 | 114 | // elements 115 | for _, e := range elements { 116 | if err := d.deserializeByElement(e); err != nil { 117 | return err 118 | } 119 | } 120 | return nil 121 | } 122 | 123 | func (d *delayDeserializer) deserializeByElement(element interface{}) error { 124 | 125 | t := reflect.ValueOf(element) 126 | if t.Kind() != reflect.Ptr { 127 | return fmt.Errorf("element must set pointer value. but got: %t", element) 128 | } 129 | 130 | t = t.Elem() 131 | if t.Kind() == reflect.Ptr { 132 | t = t.Elem() 133 | } 134 | 135 | // address 136 | address := t.Addr().Pointer() 137 | return d.deserializeByAddress(address) 138 | } 139 | 140 | func (d *delayDeserializer) deserializeByAddress(address uintptr) error { 141 | index, ok := d.processedMap[address] 142 | if !ok { 143 | return fmt.Errorf("not found address: %v", address) 144 | } 145 | 146 | // already deserialized 147 | if index < 0 { 148 | return nil 149 | } 150 | 151 | // value 152 | rv := d.holder.Field(index) 153 | // offset 154 | off := 8 + uint32(index)*byte4 155 | b, _ := d.readSize4(off) 156 | dataIndex := binary.LittleEndian.Uint32(b) 157 | 158 | // deserialize and update flag 159 | d.deserialize(rv, dataIndex) 160 | d.processedMap[address] = -1 161 | return nil 162 | } 163 | 164 | func (d *delayDeserializer) IsDeserialized(element interface{}) (bool, error) { 165 | 166 | t := reflect.ValueOf(element) 167 | if t.Kind() != reflect.Ptr { 168 | return false, fmt.Errorf("holder must set pointer value. but got: %t", element) 169 | } 170 | 171 | t = t.Elem() 172 | if t.Kind() == reflect.Ptr { 173 | t = t.Elem() 174 | } 175 | 176 | // address 177 | address := t.Addr().Pointer() 178 | 179 | index, ok := d.processedMap[address] 180 | if !ok { 181 | return false, fmt.Errorf("not found element: %t", element) 182 | } 183 | 184 | return (index < 0), nil 185 | } 186 | -------------------------------------------------------------------------------- /read.go: -------------------------------------------------------------------------------- 1 | package zeroformatter 2 | 3 | func (d *deserializer) readSize1(index uint32) (byte, uint32) { 4 | rb := byte1 5 | return d.data[index], index + rb 6 | } 7 | 8 | func (d *deserializer) readSize2(index uint32) ([]byte, uint32) { 9 | rb := byte2 10 | return d.data[index : index+rb], index + rb 11 | } 12 | 13 | func (d *deserializer) readSize4(index uint32) ([]byte, uint32) { 14 | rb := byte4 15 | return d.data[index : index+rb], index + rb 16 | } 17 | 18 | func (d *deserializer) readSize8(index uint32) ([]byte, uint32) { 19 | rb := byte8 20 | return d.data[index : index+rb], index + rb 21 | } 22 | -------------------------------------------------------------------------------- /serialize.go: -------------------------------------------------------------------------------- 1 | package zeroformatter 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math" 7 | "reflect" 8 | "unicode/utf16" 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | byte1 uint32 = 1 << iota 14 | byte2 15 | byte4 16 | byte8 17 | ) 18 | 19 | type serializer struct { 20 | create []byte 21 | 22 | queueMapKey [][]reflect.Value 23 | queueMapValue []reflect.Value 24 | } 25 | 26 | func createSerializer() *serializer { 27 | return &serializer{ 28 | queueMapKey: [][]reflect.Value{}, 29 | queueMapValue: []reflect.Value{}, 30 | } 31 | } 32 | 33 | // Serialize analyzes holder and converts to byte datas. 34 | func Serialize(holder interface{}) ([]byte, error) { 35 | d := createSerializer() 36 | 37 | t := reflect.ValueOf(holder) 38 | if t.Kind() == reflect.Ptr { 39 | t = t.Elem() 40 | if t.Kind() == reflect.Ptr { 41 | t = t.Elem() 42 | } 43 | } 44 | 45 | var err error 46 | if t.Kind() == reflect.Struct && !isDateTime(t) && !isDateTimeOffset(t) { 47 | startOffset := uint32(2+t.NumField()) * byte4 48 | 49 | dataPartSize, _ := d.calcSize(t) 50 | size := startOffset + dataPartSize 51 | d.create = make([]byte, size) 52 | 53 | err = d.serializeStruct(t, startOffset, size) 54 | } else { 55 | size, _ := d.calcSize(t) 56 | d.create = make([]byte, size) 57 | _, err = d.serialize(t, 0) 58 | } 59 | 60 | return d.create, err 61 | } 62 | 63 | func (d *serializer) serializeStruct(rv reflect.Value, offset uint32, size uint32) error { 64 | nf := rv.NumField() 65 | index := 2 * byte4 66 | for i := 0; i < nf; i++ { 67 | s, err := d.serialize(rv.Field(i), offset) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | d.create[index], d.create[index+1], d.create[index+2], d.create[index+3] = byte(offset), byte(offset>>8), byte(offset>>16), byte(offset>>24) 73 | index += byte4 74 | offset += s 75 | } 76 | // size 77 | d.create[0], d.create[1], d.create[2], d.create[3] = byte(size), byte(size>>8), byte(size>>16), byte(size>>24) 78 | // last index 79 | li := nf - 1 80 | d.create[4], d.create[5], d.create[6], d.create[7] = byte(li), byte(li>>8), byte(li>>16), byte(li>>24) 81 | return nil 82 | } 83 | 84 | func (d *serializer) isFixedSize(rv reflect.Value) bool { 85 | ret := false 86 | switch rv.Kind() { 87 | case 88 | reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, 89 | reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, 90 | reflect.Float32, reflect.Float64, 91 | reflect.Bool: 92 | ret = true 93 | 94 | case 95 | reflect.Struct: 96 | if isDateTimeOffset(rv) || isDateTime(rv) { 97 | ret = true 98 | } 99 | 100 | default: 101 | } 102 | return ret 103 | } 104 | 105 | func (d *serializer) calcSize(rv reflect.Value) (uint32, error) { 106 | ret := uint32(0) 107 | 108 | switch rv.Kind() { 109 | case reflect.Int8: 110 | ret = byte1 111 | 112 | case reflect.Int16: 113 | ret = byte2 114 | 115 | case reflect.Int32: 116 | if isChar(rv) { 117 | ret = byte2 118 | } else { 119 | ret = byte4 120 | } 121 | 122 | case reflect.Int: 123 | ret = byte4 124 | 125 | case reflect.Int64: 126 | if isDuration(rv) { 127 | ret = byte4 + byte8 128 | } else { 129 | ret = byte8 130 | } 131 | 132 | case reflect.Uint8: 133 | ret = byte1 134 | 135 | case reflect.Uint16: 136 | ret = byte2 137 | 138 | case reflect.Uint32, reflect.Uint: 139 | ret = byte4 140 | 141 | case reflect.Uint64: 142 | ret = byte8 143 | 144 | case reflect.Float32: 145 | ret = byte4 146 | 147 | case reflect.Float64: 148 | ret = byte8 149 | 150 | case reflect.Bool: 151 | ret = byte1 152 | 153 | case reflect.String: 154 | l := uint32(rv.Len()) 155 | ret = l + byte4 156 | 157 | case reflect.Array, reflect.Slice: 158 | l := rv.Len() 159 | if l > 0 { 160 | ret += byte4 161 | isTypeFixed := d.isFixedSize(rv.Index(0)) 162 | if isTypeFixed { 163 | s, err := d.calcSize(rv.Index(0)) 164 | if err != nil { 165 | return 0, err 166 | } 167 | ret += s * uint32(l) 168 | } else { 169 | for i := 0; i < l; i++ { 170 | s, err := d.calcSize(rv.Index(i)) 171 | if err != nil { 172 | return 0, err 173 | } 174 | ret += s 175 | } 176 | } 177 | } else { 178 | // only length info 179 | ret = byte4 180 | } 181 | 182 | case reflect.Struct: 183 | if isDateTimeOffset(rv) { 184 | ret = byte4 + byte8 + byte2 185 | } else if isDateTime(rv) { 186 | ret = byte4 + byte8 187 | } else { 188 | for i := 0; i < rv.NumField(); i++ { 189 | s, err := d.calcSize(rv.Field(i)) 190 | if err != nil { 191 | return 0, err 192 | } 193 | ret += s 194 | } 195 | } 196 | 197 | case reflect.Map: 198 | // length 199 | ret += byte4 200 | l := uint32(rv.Len()) 201 | 202 | if l < 1 { 203 | return ret, nil 204 | } 205 | // check fixed type 206 | keys := rv.MapKeys() 207 | 208 | d.queueMapKey = append(d.queueMapKey, keys) 209 | 210 | isFixedKey := d.isFixedSize(keys[0]) 211 | if isFixedKey { 212 | sizeK, err := d.calcSize(keys[0]) 213 | if err != nil { 214 | return 0, err 215 | } 216 | startI := len(d.queueMapValue) 217 | add := make([]reflect.Value, l) 218 | d.queueMapValue = append(d.queueMapValue, add...) 219 | for i, k := range keys { 220 | value := rv.MapIndex(k) 221 | d.queueMapValue[startI+i] = value 222 | sizeV, err := d.calcSize(value) 223 | if err != nil { 224 | return 0, err 225 | } 226 | ret += sizeK + sizeV 227 | } 228 | 229 | } else { 230 | startI := len(d.queueMapValue) 231 | add := make([]reflect.Value, l) 232 | d.queueMapValue = append(d.queueMapValue, add...) 233 | for i, k := range keys { 234 | sizeK, err := d.calcSize(k) 235 | if err != nil { 236 | return 0, err 237 | } 238 | value := rv.MapIndex(k) 239 | d.queueMapValue[startI+i] = value 240 | sizeV, err := d.calcSize(value) 241 | if err != nil { 242 | return 0, err 243 | } 244 | ret += sizeK + sizeV 245 | } 246 | } 247 | 248 | case reflect.Ptr: 249 | if rv.IsNil() { 250 | return 0, errors.New(fmt.Sprint("pointer is null : ", rv.Type())) 251 | } 252 | s, err := d.calcSize(rv.Elem()) 253 | if err != nil { 254 | return 0, err 255 | } 256 | ret = s 257 | 258 | default: 259 | return 0, errors.New(fmt.Sprint("this type is not supported : ", rv.Type())) 260 | } 261 | 262 | return ret, nil 263 | } 264 | 265 | func (d *serializer) serialize(rv reflect.Value, offset uint32) (uint32, error) { 266 | size := uint32(0) 267 | 268 | switch rv.Kind() { 269 | case reflect.Int8: 270 | d.writeSize1Int64(rv.Int(), offset) 271 | size += byte1 272 | 273 | case reflect.Int16: 274 | d.writeSize2Int64(rv.Int(), offset) 275 | size += byte2 276 | 277 | case reflect.Int32: 278 | if isChar(rv) { 279 | 280 | // rune [ushort(2)] 281 | enc := utf16.Encode([]rune{int32(rv.Int())}) 282 | v := enc[0] 283 | d.create[offset+0] = byte(v) 284 | d.create[offset+1] = byte(v >> 8) 285 | size += byte2 286 | } else { 287 | d.writeSize4Int64(rv.Int(), offset) 288 | size += byte4 289 | } 290 | 291 | case reflect.Int: 292 | d.writeSize4Int64(rv.Int(), offset) 293 | size += byte4 294 | 295 | case reflect.Int64: 296 | if isDuration(rv) { 297 | // seconds 298 | ns := rv.MethodByName("Nanoseconds").Call([]reflect.Value{})[0] 299 | nanoseconds := ns.Int() 300 | sec, nsec := nanoseconds/(1000*1000), int64(nanoseconds%(1000*1000)) 301 | d.writeSize8Int64(sec, offset) 302 | size += byte8 303 | offset += byte8 304 | 305 | // nanos 306 | d.writeSize4Int64(nsec, offset) 307 | size += byte4 308 | } else { 309 | d.writeSize8Int64(rv.Int(), offset) 310 | size += byte8 311 | } 312 | 313 | case reflect.Uint8: 314 | d.writeSize1Uint64(rv.Uint(), offset) 315 | size += byte1 316 | 317 | case reflect.Uint16: 318 | d.writeSize2Uint64(rv.Uint(), offset) 319 | size += byte2 320 | 321 | case reflect.Uint32, reflect.Uint: 322 | d.writeSize4Uint64(rv.Uint(), offset) 323 | size += byte4 324 | 325 | case reflect.Uint64: 326 | d.writeSize8Uint64(rv.Uint(), offset) 327 | size += byte8 328 | 329 | case reflect.Float32: 330 | v := math.Float32bits(float32(rv.Float())) 331 | d.writeSize4Uint32(v, offset) 332 | size += byte4 333 | 334 | case reflect.Float64: 335 | v := math.Float64bits(rv.Float()) 336 | d.writeSize8Uint64(v, offset) 337 | size += byte8 338 | 339 | case reflect.Bool: 340 | 341 | if rv.Bool() { 342 | d.writeSize1Uint64(0x01, offset) 343 | } else { 344 | d.writeSize1Uint64(0x00, offset) 345 | } 346 | size += byte1 347 | 348 | case reflect.String: 349 | str := rv.String() 350 | l := uint32(len(str)) 351 | d.writeSize4Uint32(l, offset) 352 | size += byte4 353 | offset += byte4 354 | 355 | // NOTE : unsafe 356 | strBytes := *(*[]byte)(unsafe.Pointer(&str)) 357 | for i := uint32(0); i < l; i++ { 358 | d.create[offset+i] = strBytes[i] 359 | } 360 | size += l 361 | 362 | case reflect.Array, reflect.Slice: 363 | l := rv.Len() 364 | if l > 0 { 365 | d.writeSize4Int(l, offset) 366 | size += byte4 367 | offset += byte4 368 | 369 | for i := 0; i < l; i++ { 370 | s, err := d.serialize(rv.Index(i), offset) 371 | if err != nil { 372 | return 0, err 373 | } 374 | offset += s 375 | size += s 376 | } 377 | } else { 378 | // only make length info 379 | d.writeSize4Int(0, offset) 380 | size += byte4 381 | } 382 | 383 | case reflect.Struct: 384 | if isDateTimeOffset(rv) { 385 | 386 | // offset 387 | rets := rv.MethodByName("Zone").Call([]reflect.Value{}) 388 | _, offSec := rets[0] /*name*/, rets[1].Int() /*offset*/ 389 | offMin := offSec / 60 390 | 391 | // seconds 392 | rets = rv.MethodByName("Unix").Call([]reflect.Value{}) 393 | seconds := rets[0].Int() + offSec 394 | 395 | // nanos 396 | rets = rv.MethodByName("Nanosecond").Call([]reflect.Value{}) 397 | nanos := rets[0].Int() 398 | 399 | // seconds to byte 400 | d.writeSize8Int64(seconds, offset) 401 | size += byte8 402 | offset += byte8 403 | 404 | // nanos to byte 405 | d.writeSize4Int64(nanos, offset) 406 | size += byte4 407 | offset += byte4 408 | 409 | // offset to byte 410 | d.writeSize2Int64(offMin, offset) 411 | size += byte2 412 | } else if isDateTime(rv) { 413 | // seconds 414 | unixTime := rv.MethodByName("Unix").Call([]reflect.Value{}) 415 | sec := unixTime[0].Int() 416 | d.writeSize8Int64(sec, offset) 417 | size += byte8 418 | offset += byte8 419 | 420 | // nanos 421 | rets := rv.MethodByName("Nanosecond").Call([]reflect.Value{}) 422 | nsec := rets[0].Int() 423 | d.writeSize4Int64(nsec, offset) 424 | size += byte4 425 | } else { 426 | for i := 0; i < rv.NumField(); i++ { 427 | s, err := d.serialize(rv.Field(i), offset) 428 | if err != nil { 429 | return 0, err 430 | } 431 | offset += s 432 | size += s 433 | } 434 | } 435 | 436 | case reflect.Map: 437 | // length 438 | l := rv.Len() 439 | d.writeSize4Int(l, offset) 440 | size += byte4 441 | offset += byte4 442 | 443 | if l < 1 { 444 | return size, nil 445 | } 446 | 447 | keys := d.queueMapKey[0] 448 | keysLen := len(keys) 449 | values := d.queueMapValue[:keysLen] 450 | 451 | for i, k := range keys { 452 | addOffByK, err := d.serialize(k, offset) 453 | if err != nil { 454 | return 0, err 455 | } 456 | addOffByV, err := d.serialize(values[i], offset+addOffByK) 457 | if err != nil { 458 | return 0, err 459 | } 460 | offset += addOffByK + addOffByV 461 | size += addOffByK + addOffByV 462 | } 463 | 464 | // update queue 465 | if len(d.queueMapKey) > 0 { 466 | d.queueMapKey = d.queueMapKey[1:] 467 | d.queueMapValue = d.queueMapValue[keysLen:] 468 | } else { 469 | d.queueMapKey = d.queueMapKey[:0] 470 | d.queueMapValue = d.queueMapValue[:0] 471 | } 472 | 473 | case reflect.Ptr: 474 | if rv.IsNil() { 475 | return 0, errors.New(fmt.Sprint("pointer is null : ", rv.Type())) 476 | } 477 | s, err := d.serialize(rv.Elem(), offset) 478 | if err != nil { 479 | return 0, err 480 | } 481 | size += s 482 | 483 | default: 484 | return 0, errors.New(fmt.Sprint("this type is not supported : ", rv.Type())) 485 | } 486 | 487 | return size, nil 488 | } 489 | -------------------------------------------------------------------------------- /write.go: -------------------------------------------------------------------------------- 1 | package zeroformatter 2 | 3 | func (s *serializer) writeSize1Int64(value int64, offset uint32) { 4 | s.create[offset] = byte(value) 5 | } 6 | 7 | func (s *serializer) writeSize2Int64(value int64, offset uint32) { 8 | s.create[offset] = byte(value) 9 | s.create[offset+1] = byte(value >> 8) 10 | } 11 | 12 | func (s *serializer) writeSize4Int64(value int64, offset uint32) { 13 | s.create[offset] = byte(value) 14 | s.create[offset+1] = byte(value >> 8) 15 | s.create[offset+2] = byte(value >> 16) 16 | s.create[offset+3] = byte(value >> 24) 17 | } 18 | 19 | func (s *serializer) writeSize8Int64(value int64, offset uint32) { 20 | s.create[offset] = byte(value) 21 | s.create[offset+1] = byte(value >> 8) 22 | s.create[offset+2] = byte(value >> 16) 23 | s.create[offset+3] = byte(value >> 24) 24 | s.create[offset+4] = byte(value >> 32) 25 | s.create[offset+5] = byte(value >> 40) 26 | s.create[offset+6] = byte(value >> 48) 27 | s.create[offset+7] = byte(value >> 56) 28 | } 29 | 30 | func (s *serializer) writeSize1Uint64(value uint64, offset uint32) { 31 | s.create[offset] = byte(value) 32 | } 33 | 34 | func (s *serializer) writeSize2Uint64(value uint64, offset uint32) { 35 | s.create[offset] = byte(value) 36 | s.create[offset+1] = byte(value >> 8) 37 | } 38 | 39 | func (s *serializer) writeSize4Uint64(value uint64, offset uint32) { 40 | s.create[offset] = byte(value) 41 | s.create[offset+1] = byte(value >> 8) 42 | s.create[offset+2] = byte(value >> 16) 43 | s.create[offset+3] = byte(value >> 24) 44 | } 45 | 46 | func (s *serializer) writeSize8Uint64(value uint64, offset uint32) { 47 | s.create[offset] = byte(value) 48 | s.create[offset+1] = byte(value >> 8) 49 | s.create[offset+2] = byte(value >> 16) 50 | s.create[offset+3] = byte(value >> 24) 51 | s.create[offset+4] = byte(value >> 32) 52 | s.create[offset+5] = byte(value >> 40) 53 | s.create[offset+6] = byte(value >> 48) 54 | s.create[offset+7] = byte(value >> 56) 55 | } 56 | 57 | func (s *serializer) writeSize4Int(value int, offset uint32) { 58 | s.create[offset] = byte(value) 59 | s.create[offset+1] = byte(value >> 8) 60 | s.create[offset+2] = byte(value >> 16) 61 | s.create[offset+3] = byte(value >> 24) 62 | } 63 | 64 | func (s *serializer) writeSize4Uint32(value uint32, offset uint32) { 65 | s.create[offset] = byte(value) 66 | s.create[offset+1] = byte(value >> 8) 67 | s.create[offset+2] = byte(value >> 16) 68 | s.create[offset+3] = byte(value >> 24) 69 | } 70 | -------------------------------------------------------------------------------- /zf_delay_test.go: -------------------------------------------------------------------------------- 1 | package zeroformatter_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "reflect" 8 | 9 | "github.com/shamaton/zeroformatter" 10 | "github.com/shamaton/zeroformatter/char" 11 | "github.com/shamaton/zeroformatter/datetimeoffset" 12 | ) 13 | 14 | func TestDelayDeserialize(t *testing.T) { 15 | type child3 struct { 16 | Int int 17 | } 18 | type child2 struct { 19 | Int2Uint map[int]uint 20 | Float2Bool map[float32]bool 21 | Char2String map[char.Char]string 22 | Time2TimeOffset map[time.Time]datetimeoffset.DateTimeOffset 23 | Duration2Struct map[time.Duration]child3 24 | } 25 | type child struct { 26 | IntArray []int 27 | UintArray []uint 28 | FloatArray []float32 29 | BoolArray []bool 30 | CharArray []char.Char 31 | StringArray []string 32 | TimeArray []time.Time 33 | TimeOffsetArray []datetimeoffset.DateTimeOffset 34 | DurationArray []time.Duration 35 | Child child2 36 | } 37 | type st struct { 38 | Int8 int8 39 | Int16 int16 40 | Int32 int32 41 | Int64 int64 42 | Uint8 byte 43 | Uint16 uint16 44 | Uint32 uint32 45 | Uint64 uint64 46 | Float float32 47 | Double float64 48 | Bool bool 49 | Char char.Char 50 | String string 51 | Time time.Time 52 | Duration time.Duration 53 | TimeOffset datetimeoffset.DateTimeOffset 54 | Child child 55 | } 56 | vSt := &st{ 57 | Int32: -32, 58 | Int8: -8, 59 | Int16: -16, 60 | Int64: -64, 61 | Uint32: 32, 62 | Uint8: 8, 63 | Uint16: 16, 64 | Uint64: 64, 65 | Float: 1.23, 66 | Double: 2.3456, 67 | Bool: true, 68 | Char: char.Char('A'), 69 | String: "Parent", 70 | Time: now, 71 | Duration: time.Duration(123 * time.Second), 72 | TimeOffset: datetimeoffset.Now(), 73 | 74 | // child 75 | Child: child{ 76 | IntArray: []int{-1, -2, -3, -4, -5}, 77 | UintArray: []uint{1, 2, 3, 4, 5}, 78 | FloatArray: []float32{-1.2, -3.4, -5.6, -7.8}, 79 | BoolArray: []bool{true, true, false, false, true}, 80 | CharArray: []char.Char{char.Char('X'), char.Char('Y'), char.Char('Z')}, 81 | StringArray: []string{"str", "ing", "arr", "ay"}, 82 | TimeArray: []time.Time{now, now, now}, 83 | TimeOffsetArray: []datetimeoffset.DateTimeOffset{datetimeoffset.Now(), datetimeoffset.Now(), datetimeoffset.Now()}, 84 | DurationArray: []time.Duration{time.Duration(1 * time.Nanosecond), time.Duration(2 * time.Nanosecond)}, 85 | 86 | // childchild 87 | Child: child2{ 88 | Int2Uint: map[int]uint{-1: 2, -3: 4}, 89 | Float2Bool: map[float32]bool{-1.1: true, -2.2: false}, 90 | Char2String: map[char.Char]string{char.Char('A'): "AA", char.Char('B'): "BB"}, 91 | Time2TimeOffset: map[time.Time]datetimeoffset.DateTimeOffset{now: datetimeoffset.Now()}, 92 | Duration2Struct: map[time.Duration]child3{time.Duration(1 * time.Hour): child3{Int: 1}, time.Duration(2 * time.Hour): child3{Int: 2}}, 93 | }, 94 | }, 95 | } 96 | 97 | b, err := zeroformatter.Serialize(vSt) 98 | if err != nil { 99 | t.Error(err) 100 | } 101 | 102 | eHolder := &st{} 103 | dds, err := zeroformatter.DelayDeserialize(eHolder, b) 104 | if err != nil { 105 | t.Error(err) 106 | } 107 | 108 | // simple 109 | if err := dds.DeserializeByElement(&eHolder.Int8); err != nil { 110 | t.Error(err) 111 | } 112 | if err := dds.DeserializeByElement(&eHolder.Uint8); err != nil { 113 | t.Error(err) 114 | } 115 | if err := dds.DeserializeByElement(&eHolder.Float); err != nil { 116 | t.Error(err) 117 | } 118 | if err := dds.DeserializeByElement(&eHolder.Char); err != nil { 119 | t.Error(err) 120 | } 121 | if err := dds.DeserializeByElement(&eHolder.Time); err != nil { 122 | t.Error(err) 123 | } 124 | 125 | if _bool, err := dds.IsDeserialized(&eHolder.Int16); err != nil || _bool { 126 | t.Error("deserialized error") 127 | } 128 | 129 | // multiple 130 | if err := dds.DeserializeByElement(&eHolder.Int16, &eHolder.Int32, &eHolder.Int64); err != nil { 131 | t.Error(err) 132 | } 133 | if err := dds.DeserializeByElement(&eHolder.Uint16, &eHolder.Uint32, &eHolder.Uint64); err != nil { 134 | t.Error(err) 135 | } 136 | if err := dds.DeserializeByElement(&eHolder.Double, &eHolder.String, &eHolder.Bool); err != nil { 137 | t.Error(err) 138 | } 139 | if err := dds.DeserializeByElement(&eHolder.Duration, &eHolder.TimeOffset, &eHolder.Child); err != nil { 140 | t.Error(err) 141 | } 142 | 143 | if _bool, err := dds.IsDeserialized(&eHolder.Int16); err != nil || !_bool { 144 | t.Error("deserialized error") 145 | } 146 | 147 | // value equal ? 148 | if !reflect.DeepEqual(vSt, eHolder) { 149 | t.Error("value not equal!!") 150 | } 151 | 152 | iHolder := &st{} 153 | 154 | dds, err = zeroformatter.DelayDeserialize(iHolder, b) 155 | if err != nil { 156 | t.Error(err) 157 | } 158 | if err := dds.DeserializeByIndex(0); err != nil { 159 | t.Error(err) 160 | } 161 | if err := dds.DeserializeByIndex(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); err != nil { 162 | t.Error(err) 163 | } 164 | 165 | if err := dds.DeserializeByIndex(17); err == nil { 166 | t.Error("index error") 167 | } 168 | 169 | // value equal ? 170 | if !reflect.DeepEqual(vSt, iHolder) { 171 | t.Error("value not equal!!") 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /zf_test.go: -------------------------------------------------------------------------------- 1 | package zeroformatter_test 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math" 7 | "reflect" 8 | "strings" 9 | "testing" 10 | "time" 11 | 12 | "github.com/shamaton/zeroformatter" 13 | "github.com/shamaton/zeroformatter/char" 14 | "github.com/shamaton/zeroformatter/datetimeoffset" 15 | ) 16 | 17 | var now time.Time 18 | 19 | func init() { 20 | n := time.Now() 21 | now = time.Unix(n.Unix(), int64(n.Nanosecond())) 22 | } 23 | func TestPrimitiveInt(t *testing.T) { 24 | var rInt8 int8 25 | vInt8 := int8(-8) 26 | if err := checkRoutine(t, vInt8, &rInt8, false); err != nil { 27 | t.Error(err) 28 | } 29 | 30 | var rInt16 int16 31 | vInt16 := int16(-16) 32 | if err := checkRoutine(t, vInt16, &rInt16, false); err != nil { 33 | t.Error(err) 34 | } 35 | 36 | var rInt int 37 | vInt := -65535 38 | if err := checkRoutine(t, vInt, &rInt, false); err != nil { 39 | t.Error(err) 40 | } 41 | 42 | var rInt32 int32 43 | vInt32 := int32(-32) 44 | if err := checkRoutine(t, vInt32, &rInt32, false); err != nil { 45 | t.Error(err) 46 | } 47 | 48 | var rInt64 int64 49 | vInt64 := int64(-64) 50 | if err := checkRoutine(t, vInt64, &rInt64, false); err != nil { 51 | t.Error(err) 52 | } 53 | 54 | // pointer 55 | pvInt8 := new(int8) 56 | prInt8 := new(int8) 57 | *pvInt8 = math.MaxInt8 58 | if err := checkRoutine(t, pvInt8, prInt8, false); err != nil { 59 | t.Error(err) 60 | } 61 | if err := checkRoutine(t, &pvInt8, &prInt8, false); err != nil { 62 | t.Error(err) 63 | } 64 | 65 | pvInt16 := new(int16) 66 | prInt16 := new(int16) 67 | *pvInt16 = math.MaxInt16 68 | if err := checkRoutine(t, pvInt16, prInt16, false); err != nil { 69 | t.Error(err) 70 | } 71 | if err := checkRoutine(t, &pvInt16, &prInt16, false); err != nil { 72 | t.Error(err) 73 | } 74 | 75 | pvInt := new(int) 76 | prInt := new(int) 77 | *pvInt = math.MaxInt32 78 | if err := checkRoutine(t, pvInt, prInt, false); err != nil { 79 | t.Error(err) 80 | } 81 | if err := checkRoutine(t, &pvInt, &prInt, false); err != nil { 82 | t.Error(err) 83 | } 84 | 85 | pvInt32 := new(int32) 86 | prInt32 := new(int32) 87 | *pvInt32 = math.MaxInt32 88 | if err := checkRoutine(t, pvInt32, prInt32, false); err != nil { 89 | t.Error(err) 90 | } 91 | if err := checkRoutine(t, &pvInt32, &prInt32, false); err != nil { 92 | t.Error(err) 93 | } 94 | 95 | pvInt64 := new(int64) 96 | prInt64 := new(int64) 97 | *pvInt64 = math.MaxInt64 98 | if err := checkRoutine(t, pvInt64, prInt64, false); err != nil { 99 | t.Error(err) 100 | } 101 | if err := checkRoutine(t, &pvInt64, &prInt64, false); err != nil { 102 | t.Error(err) 103 | } 104 | 105 | // error 106 | var rError int32 107 | vError := int(-1) 108 | if err := checkRoutine(t, vError, &rError, false); err != nil { 109 | if strings.Contains(err.Error(), "value diffrent") { 110 | t.Error(err) 111 | } 112 | } 113 | } 114 | 115 | func TestPrimitiveUint(t *testing.T) { 116 | var rUint8 uint8 117 | vUint8 := uint8(math.MaxUint8) 118 | if err := checkRoutine(t, vUint8, &rUint8, false); err != nil { 119 | t.Error(err) 120 | } 121 | 122 | var rUint16 uint16 123 | vUint16 := uint16(math.MaxUint16) 124 | if err := checkRoutine(t, vUint16, &rUint16, false); err != nil { 125 | t.Error(err) 126 | } 127 | 128 | var rUint uint 129 | vUint := uint(math.MaxUint32 / 2) 130 | if err := checkRoutine(t, vUint, &rUint, false); err != nil { 131 | t.Error(err) 132 | } 133 | 134 | var rUint32 uint32 135 | vUint32 := uint32(math.MaxUint32) 136 | if err := checkRoutine(t, vUint32, &rUint32, false); err != nil { 137 | t.Error(err) 138 | } 139 | 140 | var rUint64 uint64 141 | vUint64 := uint64(math.MaxUint64) 142 | if err := checkRoutine(t, vUint64, &rUint64, false); err != nil { 143 | t.Error(err) 144 | } 145 | 146 | // pointer 147 | pvUint8 := new(uint8) 148 | prUint8 := new(uint8) 149 | *pvUint8 = math.MaxUint8 150 | if err := checkRoutine(t, pvUint8, prUint8, false); err != nil { 151 | t.Error(err) 152 | } 153 | if err := checkRoutine(t, &pvUint8, &prUint8, false); err != nil { 154 | t.Error(err) 155 | } 156 | 157 | pvUint16 := new(uint16) 158 | prUint16 := new(uint16) 159 | *pvUint16 = math.MaxUint16 160 | if err := checkRoutine(t, pvUint16, prUint16, false); err != nil { 161 | t.Error(err) 162 | } 163 | if err := checkRoutine(t, &pvUint16, &prUint16, false); err != nil { 164 | t.Error(err) 165 | } 166 | 167 | pvUint := new(uint) 168 | prUint := new(uint) 169 | *pvUint = math.MaxUint32 170 | if err := checkRoutine(t, pvUint, prUint, false); err != nil { 171 | t.Error(err) 172 | } 173 | if err := checkRoutine(t, &pvUint, &prUint, false); err != nil { 174 | t.Error(err) 175 | } 176 | 177 | pvUint32 := new(uint32) 178 | prUint32 := new(uint32) 179 | *pvUint32 = math.MaxUint32 180 | if err := checkRoutine(t, pvUint32, prUint32, false); err != nil { 181 | t.Error(err) 182 | } 183 | if err := checkRoutine(t, &pvUint32, &prUint32, false); err != nil { 184 | t.Error(err) 185 | } 186 | 187 | pvUint64 := new(uint64) 188 | prUint64 := new(uint64) 189 | *pvUint64 = math.MaxUint64 190 | if err := checkRoutine(t, pvUint64, prUint64, false); err != nil { 191 | t.Error(err) 192 | } 193 | if err := checkRoutine(t, &pvUint64, &prUint64, false); err != nil { 194 | t.Error(err) 195 | } 196 | 197 | var rError uint32 198 | vError := uint(1) 199 | if err := checkRoutine(t, vError, &rError, false); err != nil { 200 | if strings.Contains(err.Error(), "value diffrent") { 201 | t.Error(err) 202 | } 203 | } 204 | } 205 | 206 | func TestPrimitiveFloat(t *testing.T) { 207 | 208 | var rFloat32 float32 209 | vFloat32 := float32(math.MaxFloat32) 210 | if err := checkRoutine(t, vFloat32, &rFloat32, false); err != nil { 211 | t.Error(err) 212 | } 213 | 214 | var rFloat64 float64 215 | vFloat64 := math.MaxFloat64 216 | if err := checkRoutine(t, vFloat64, &rFloat64, false); err != nil { 217 | t.Error(err) 218 | } 219 | 220 | // pointer 221 | pvFloat32 := new(float32) 222 | prFloat32 := new(float32) 223 | *pvFloat32 = math.MaxFloat32 / 2 224 | if err := checkRoutine(t, pvFloat32, prFloat32, false); err != nil { 225 | t.Error(err) 226 | } 227 | if err := checkRoutine(t, &pvFloat32, &prFloat32, false); err != nil { 228 | t.Error(err) 229 | } 230 | 231 | pvFloat64 := new(float64) 232 | prFloat64 := new(float64) 233 | *pvFloat64 = math.MaxFloat64 / 2 234 | if err := checkRoutine(t, pvFloat64, prFloat64, false); err != nil { 235 | t.Error(err) 236 | } 237 | if err := checkRoutine(t, &pvFloat64, &prFloat64, false); err != nil { 238 | t.Error(err) 239 | } 240 | 241 | // error 242 | var rError float32 243 | vError := float64(1) 244 | if err := checkRoutine(t, vError, &rError, false); err != nil { 245 | if strings.Contains(err.Error(), "value diffrent") { 246 | t.Error(err) 247 | } 248 | } 249 | } 250 | 251 | func TestPrimitiveBool(t *testing.T) { 252 | var rBool bool 253 | vBool := true 254 | if err := checkRoutine(t, vBool, &rBool, false); err != nil { 255 | t.Error(err) 256 | } 257 | 258 | pvBool := new(bool) 259 | prBool := new(bool) 260 | *pvBool = true 261 | if err := checkRoutine(t, pvBool, prBool, false); err != nil { 262 | t.Error(err) 263 | } 264 | if err := checkRoutine(t, &pvBool, &prBool, false); err != nil { 265 | t.Error(err) 266 | } 267 | } 268 | 269 | func TestPrimitiveString(t *testing.T) { 270 | 271 | var rChar char.Char 272 | vChar := char.Char('Z') 273 | if err := checkRoutine(t, vChar, &rChar, false); err != nil { 274 | t.Error(err) 275 | } 276 | //t.Logf("%#U", rChar) 277 | 278 | var rString string 279 | vString := "this string serialize and deserialize." 280 | if err := checkRoutine(t, vString, &rString, false); err != nil { 281 | t.Error(err) 282 | } 283 | 284 | // pointer 285 | pvChar := new(char.Char) 286 | prChar := new(char.Char) 287 | *pvChar = char.Char('Y') 288 | if err := checkRoutine(t, pvChar, prChar, false); err != nil { 289 | t.Error(err) 290 | } 291 | if err := checkRoutine(t, &pvChar, &prChar, false); err != nil { 292 | t.Error(err) 293 | } 294 | //t.Logf("%#U", *prChar) 295 | 296 | pvString := new(string) 297 | prString := new(string) 298 | *pvString = "this string is pointer value" 299 | if err := checkRoutine(t, pvString, prString, false); err != nil { 300 | t.Error(err) 301 | } 302 | if err := checkRoutine(t, &pvString, &prString, false); err != nil { 303 | t.Error(err) 304 | } 305 | } 306 | 307 | func TestPrimitiveTime(t *testing.T) { 308 | 309 | var rTime time.Time 310 | vTime := now 311 | if err := checkRoutine(t, vTime, &rTime, false); err != nil { 312 | t.Error(err) 313 | } 314 | 315 | var rDuration time.Duration 316 | vDuration := time.Duration(12*time.Hour + 34*time.Minute + 56*time.Second + 78*time.Nanosecond) 317 | if err := checkRoutine(t, vDuration, &rDuration, false); err != nil { 318 | t.Error(err) 319 | } 320 | 321 | var rOffset datetimeoffset.DateTimeOffset 322 | vOffset := datetimeoffset.Now() 323 | if err := checkRoutine(t, vOffset, &rOffset, false); err != nil { 324 | t.Error(err) 325 | } 326 | 327 | // pointer 328 | pvTime := new(time.Time) 329 | prTime := new(time.Time) 330 | *pvTime = now 331 | if err := checkRoutine(t, pvTime, prTime, false); err != nil { 332 | t.Error(err) 333 | } 334 | if err := checkRoutine(t, &pvTime, &prTime, false); err != nil { 335 | t.Error(err) 336 | } 337 | 338 | pvDuration := new(time.Duration) 339 | prDuration := new(time.Duration) 340 | *pvDuration = time.Duration(987654321 * time.Nanosecond) 341 | if err := checkRoutine(t, pvDuration, prDuration, false); err != nil { 342 | t.Error(err) 343 | } 344 | if err := checkRoutine(t, &pvDuration, &prDuration, false); err != nil { 345 | t.Error(err) 346 | } 347 | 348 | pvOffset := new(datetimeoffset.DateTimeOffset) 349 | prOffset := new(datetimeoffset.DateTimeOffset) 350 | *pvOffset = datetimeoffset.Now() 351 | if err := checkRoutine(t, pvOffset, prOffset, false); err != nil { 352 | t.Error(err) 353 | } 354 | if err := checkRoutine(t, &pvOffset, &prOffset, false); err != nil { 355 | t.Error(err) 356 | } 357 | 358 | // error 359 | var rError time.Time 360 | vError := datetimeoffset.Now() 361 | if err := checkRoutine(t, vError, &rError, false); err != nil { 362 | if strings.Contains(err.Error(), "value diffrent") { 363 | t.Error(err) 364 | } 365 | } 366 | } 367 | 368 | func TestArray(t *testing.T) { 369 | 370 | var rIntA [10]int 371 | vIntA := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, math.MinInt32} 372 | if err := checkRoutine(t, vIntA, &rIntA, false); err != nil { 373 | t.Error(err) 374 | } 375 | 376 | var rIntS []int 377 | vIntS := []int{math.MinInt32, 9, 8, 7, 6, 5, 4, 3, 2, 1} 378 | if err := checkRoutine(t, vIntS, &rIntS, false); err != nil { 379 | t.Error(err) 380 | } 381 | 382 | var rUintA [10]uint 383 | vUintA := [10]uint{1, 2, 3, 4, 5, 6, 7, 8, 9, math.MaxUint32} 384 | if err := checkRoutine(t, vUintA, &rUintA, false); err != nil { 385 | t.Error(err) 386 | } 387 | 388 | var rUintS []uint 389 | vUintS := []uint{math.MaxUint32, 9, 8, 7, 6, 5, 4, 3, 2, 1} 390 | if err := checkRoutine(t, vUintS, &rUintS, false); err != nil { 391 | t.Error(err) 392 | } 393 | 394 | var rFloatA [5]float64 395 | vFloatA := [5]float64{1.2, 3.4, 5.6, 7.8, math.MaxFloat64} 396 | if err := checkRoutine(t, vFloatA, &rFloatA, false); err != nil { 397 | t.Error(err) 398 | } 399 | 400 | var rFloatS []float64 401 | vFloatS := []float64{math.MaxFloat64, 9.8, 7.6, 5.4, 3.2} 402 | if err := checkRoutine(t, vFloatS, &rFloatS, false); err != nil { 403 | t.Error(err) 404 | } 405 | 406 | var rBoolA [5]bool 407 | vBoolA := [5]bool{true, false, true, false, true} 408 | if err := checkRoutine(t, vBoolA, &rBoolA, false); err != nil { 409 | t.Error(err) 410 | } 411 | 412 | var rBoolS []bool 413 | vBoolS := []bool{false, true, false, true, false} 414 | if err := checkRoutine(t, vBoolS, &rBoolS, false); err != nil { 415 | t.Error(err) 416 | } 417 | 418 | var rStrA []string 419 | vStrA := []string{"this", "is", "string", "array", ".", "can", "you", "see", "?"} 420 | if err := checkRoutine(t, vStrA, &rStrA, false); err != nil { 421 | t.Error(err) 422 | } 423 | 424 | var rStrS []string 425 | vStrS := []string{"this", "is", "string", "slice", ".", "can", "you", "see", "?"} 426 | if err := checkRoutine(t, vStrS, &rStrS, false); err != nil { 427 | t.Error(err) 428 | } 429 | 430 | var rEmptyA [0]string 431 | vEmptyA := [0]string{} 432 | if err := checkRoutine(t, vEmptyA, &rEmptyA, false); err != nil { 433 | t.Error(err) 434 | } 435 | 436 | var rEmptyS []string 437 | vEmptyS := []string{} 438 | if err := checkRoutine(t, vEmptyS, &rEmptyS, false); err != nil { 439 | t.Error(err) 440 | } 441 | 442 | var rCharA [3]char.Char 443 | vCharA := [3]char.Char{'A', 'B', 'C'} 444 | if err := checkRoutine(t, vCharA, &rCharA, false); err != nil { 445 | t.Error(err) 446 | } 447 | 448 | var rCharS []char.Char 449 | vCharS := []char.Char{'C', 'B', 'A'} 450 | if err := checkRoutine(t, vCharS, &rCharS, false); err != nil { 451 | t.Error(err) 452 | } 453 | 454 | var rOffsetA [3]datetimeoffset.DateTimeOffset 455 | vOffsetA := [3]datetimeoffset.DateTimeOffset{datetimeoffset.Now(), datetimeoffset.Now(), datetimeoffset.Now()} 456 | if err := checkRoutine(t, vOffsetA, &rOffsetA, false); err != nil { 457 | t.Error(err) 458 | } 459 | 460 | var rOffsetS []datetimeoffset.DateTimeOffset 461 | vOffsetS := []datetimeoffset.DateTimeOffset{datetimeoffset.Now(), datetimeoffset.Now(), datetimeoffset.Now()} 462 | if err := checkRoutine(t, vOffsetS, &rOffsetS, false); err != nil { 463 | t.Error(err) 464 | } 465 | 466 | // pointer 467 | pvStrS := new([]string) 468 | prStrS := new([]string) 469 | *pvStrS = []string{"this", "is", "pointer", "string", "slice", ".", "can", "you", "see", "?"} 470 | if err := checkRoutine(t, pvStrS, prStrS, false); err != nil { 471 | t.Error(err) 472 | } 473 | if err := checkRoutine(t, &pvStrS, &prStrS, false); err != nil { 474 | t.Error(err) 475 | } 476 | } 477 | 478 | func TestStruct(t *testing.T) { 479 | type child3 struct { 480 | Int int 481 | } 482 | type child2 struct { 483 | Int2Uint map[int]uint 484 | Float2Bool map[float32]bool 485 | Char2String map[char.Char]string 486 | Time2TimeOffset map[time.Time]datetimeoffset.DateTimeOffset 487 | Duration2Struct map[time.Duration]child3 488 | } 489 | type child struct { 490 | IntArray []int 491 | UintArray []uint 492 | FloatArray []float32 493 | BoolArray []bool 494 | CharArray []char.Char 495 | StringArray []string 496 | TimeArray []time.Time 497 | TimeOffsetArray []datetimeoffset.DateTimeOffset 498 | DurationArray []time.Duration 499 | Child child2 500 | } 501 | type st struct { 502 | Int8 int8 503 | Int16 int16 504 | Int32 int32 505 | Int64 int64 506 | Uint8 byte 507 | Uint16 uint16 508 | Uint32 uint32 509 | Uint64 uint64 510 | Float float32 511 | Double float64 512 | Bool bool 513 | Char char.Char 514 | String string 515 | Time time.Time 516 | Duration time.Duration 517 | TimeOffset datetimeoffset.DateTimeOffset 518 | Child child 519 | } 520 | vSt := &st{ 521 | Int32: -32, 522 | Int8: -8, 523 | Int16: -16, 524 | Int64: -64, 525 | Uint32: 32, 526 | Uint8: 8, 527 | Uint16: 16, 528 | Uint64: 64, 529 | Float: 1.23, 530 | Double: 2.3456, 531 | Bool: true, 532 | Char: char.Char('A'), 533 | String: "Parent", 534 | Time: now, 535 | Duration: time.Duration(123 * time.Second), 536 | TimeOffset: datetimeoffset.Now(), 537 | 538 | // child 539 | Child: child{ 540 | IntArray: []int{-1, -2, -3, -4, -5}, 541 | UintArray: []uint{1, 2, 3, 4, 5}, 542 | FloatArray: []float32{-1.2, -3.4, -5.6, -7.8}, 543 | BoolArray: []bool{true, true, false, false, true}, 544 | CharArray: []char.Char{char.Char('X'), char.Char('Y'), char.Char('Z')}, 545 | StringArray: []string{"str", "ing", "arr", "ay"}, 546 | TimeArray: []time.Time{now, now, now}, 547 | TimeOffsetArray: []datetimeoffset.DateTimeOffset{datetimeoffset.Now(), datetimeoffset.Now(), datetimeoffset.Now()}, 548 | DurationArray: []time.Duration{time.Duration(1 * time.Nanosecond), time.Duration(2 * time.Nanosecond)}, 549 | 550 | // childchild 551 | Child: child2{ 552 | Int2Uint: map[int]uint{-1: 2, -3: 4}, 553 | Float2Bool: map[float32]bool{-1.1: true, -2.2: false}, 554 | Char2String: map[char.Char]string{char.Char('A'): "AA", char.Char('B'): "BB"}, 555 | Time2TimeOffset: map[time.Time]datetimeoffset.DateTimeOffset{now: datetimeoffset.Now()}, 556 | Duration2Struct: map[time.Duration]child3{time.Duration(1 * time.Hour): child3{Int: 1}, time.Duration(2 * time.Hour): child3{Int: 2}}, 557 | }, 558 | }, 559 | } 560 | rSt := st{} 561 | if err := checkRoutine(t, vSt, &rSt, false); err != nil { 562 | t.Error(err) 563 | } 564 | 565 | // pointer 566 | prSt := new(st) 567 | if err := checkRoutine(t, &vSt, &prSt, false); err != nil { 568 | t.Error(err) 569 | } 570 | } 571 | 572 | func TestMap(t *testing.T) { 573 | rMapInt := map[int]int{1: 2, 3: 4, math.MaxInt32: math.MinInt32} 574 | vMapInt := map[int]int{} 575 | if err := checkRoutine(t, rMapInt, &vMapInt, false); err != nil { 576 | t.Error(err) 577 | } 578 | 579 | rMapStr := map[string]float32{"this": 1.2, "is": 3.4, "float map": 56.789} 580 | vMapStr := map[string]float32{} 581 | if err := checkRoutine(t, rMapStr, &vMapStr, false); err != nil { 582 | t.Error(err) 583 | } 584 | } 585 | 586 | // for test 587 | func checkRoutine(t *testing.T, in interface{}, out interface{}, isDebug bool) error { 588 | d, err := zeroformatter.Serialize(in) 589 | if err != nil { 590 | return err 591 | } 592 | 593 | if isDebug { 594 | t.Log(in, " -- to byte --> ", d) 595 | } 596 | 597 | if err := zeroformatter.Deserialize(out, d); err != nil { 598 | return err 599 | } 600 | 601 | i := getValue(in) 602 | o := getValue(out) 603 | if isDebug { 604 | t.Log("value [in]:", i, " [out]:", o) 605 | } 606 | 607 | if !reflect.DeepEqual(i, o) { 608 | return errors.New(fmt.Sprint("value different [in]:", in, " [out]:", out)) 609 | } 610 | return nil 611 | } 612 | 613 | // for check value 614 | func getValue(v interface{}) interface{} { 615 | rv := reflect.ValueOf(v) 616 | if rv.Kind() == reflect.Ptr { 617 | rv = rv.Elem() 618 | } 619 | if rv.Kind() == reflect.Ptr { 620 | rv = rv.Elem() 621 | } 622 | return rv.Interface() 623 | } 624 | --------------------------------------------------------------------------------