github.com/glycerine/zebrapack@v4.1.1-0.20181107023619-e955d028f9bf+incompatible/zebra/util.go (about) 1 package zebra 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 8 "github.com/glycerine/zebrapack/msgp" 9 ) 10 11 func ZkindFromString(s string) Zkind { 12 s = strings.ToLower(s) 13 switch s { 14 case "": 15 return Invalid 16 case "invalid": 17 return Invalid 18 case "bytes": 19 return Bytes 20 case "string": 21 return String 22 case "float32": 23 return Float32 24 case "float64": 25 return Float64 26 case "complex64": 27 return Complex64 28 case "complex128": 29 return Complex128 30 case "uint": 31 return Uint 32 case "uint8": 33 return Uint8 34 case "uint16": 35 return Uint16 36 case "uint32": 37 return Uint32 38 case "uint64": 39 return Uint64 40 case "byte": 41 return Byte 42 case "int": 43 return Int 44 case "int8": 45 return Int8 46 case "int16": 47 return Int16 48 case "int32": 49 return Int32 50 case "int64": 51 return Int64 52 case "bool": 53 return Bool 54 case "intf": 55 return Intf 56 case "time": 57 return Time 58 case "ext": 59 return Ext 60 case "ident": 61 // IDENT typically means a named struct 62 return IDENT 63 case "baseelem": 64 return BaseElemCat 65 case "map": 66 return MapCat 67 case "struct": 68 return StructCat 69 case "slice": 70 return SliceCat 71 case "array": 72 return ArrayCat 73 case "pointer": 74 return PointerCat 75 } 76 panic(fmt.Errorf("unrecognized arg '%s' to ZkindFromString()", s)) 77 } 78 79 func (i Zkind) String() string { 80 switch i { 81 case Invalid: 82 return "" 83 case Bytes: 84 return "bytes" 85 case String: 86 return "string" 87 case Float32: 88 return "float32" 89 case Float64: 90 return "float64" 91 case Complex64: 92 return "complex64" 93 case Complex128: 94 return "complex128" 95 case Uint: 96 return "uint" 97 case Uint8: 98 return "uint8" 99 case Uint16: 100 return "uint16" 101 case Uint32: 102 return "uint32" 103 case Uint64: 104 return "uint64" 105 case Byte: 106 return "byte" 107 case Int: 108 return "int" 109 case Int8: 110 return "int8" 111 case Int16: 112 return "int16" 113 case Int32: 114 return "int32" 115 case Int64: 116 return "int64" 117 case Bool: 118 return "bool" 119 120 // compound/non-primitives are uppercased 121 // for readability 122 case Intf: 123 return "Intf" 124 case Time: 125 return "Time" 126 case Ext: 127 return "Ext" 128 case IDENT: 129 // IDENT typically means a named struct 130 return "IDENT" 131 case BaseElemCat: 132 return "BaseElem" 133 case MapCat: 134 return "Map" 135 case StructCat: 136 return "Struct" 137 case SliceCat: 138 return "Slice" 139 case ArrayCat: 140 return "Array" 141 case PointerCat: 142 return "Pointer" 143 default: 144 panic(fmt.Errorf("unrecognized Zkind value %#v", i)) 145 } 146 } 147 148 // WriteToGo writes the zebrapack schema to w as a Go source file. 149 func (s *Schema) WriteToGo(w io.Writer, path string, pkg string) (err error) { 150 if pkg == "" { 151 fmt.Fprintf(w, "\npackage %s\n\n", s.SourcePackage) 152 } else { 153 fmt.Fprintf(w, "\npackage %s\n\n", pkg) 154 } 155 fmt.Fprintf(w, "// File re-generated by: 'zebrapack -write-to-go %s'.\n", path) 156 fmt.Fprintf(w, "// The '%s' schema was originally created from: '%s'.\n\n", path, s.SourcePath) 157 158 if len(s.Imports) > 0 { 159 fmt.Fprintf(w, "import (\n") 160 } 161 for i := range s.Imports { 162 fmt.Fprintf(w, " %s\n", s.Imports[i]) 163 } 164 if len(s.Imports) > 0 { 165 fmt.Fprintf(w, ")\n\n") 166 } 167 168 fmt.Fprintf(w, "const zebraSchemaId64 = 0x%x // %v\n\n", 169 s.ZebraSchemaId, s.ZebraSchemaId) 170 171 for i := range s.Structs { 172 err = s.Structs[i].WriteToGo(w) 173 if err != nil { 174 return err 175 } 176 } 177 return nil 178 } 179 180 func (s *Struct) WriteToGo(w io.Writer) (err error) { 181 fmt.Fprintf(w, "\ntype %s struct {\n", s.StructName) 182 for _, f := range s.Fields { 183 needMsg := false 184 zid := fmt.Sprintf("`zid:\"%v\"", f.Zid) 185 msg := "msg:\"" 186 if f.FieldTagName != f.FieldGoName { 187 msg += f.FieldTagName 188 needMsg = true 189 } 190 if f.OmitEmpty { 191 msg += ",omitempty" 192 needMsg = true 193 } 194 if f.ShowZero { 195 msg += ",showzero" 196 needMsg = true 197 } 198 if f.Deprecated { 199 msg += ",deprecated" 200 needMsg = true 201 } 202 if needMsg { 203 zid += " " + msg + "\"" 204 } 205 fmt.Fprintf(w, " %s %s %s`\n", f.FieldGoName, f.FieldTypeStr, zid) 206 } 207 fmt.Fprintf(w, "}\n\n") 208 return nil 209 } 210 211 // ErrNoStructNameFound is returned by ZebraToMsgp2 when it cannot locate the 212 // embedded struct name string. 213 var ErrNoStructNameFound = fmt.Errorf("error: no -1:struct-name field:value found in zebrapack struct") 214 215 func (sch *Schema) ZebraToMsgp2(bts []byte, ignoreMissingStructName bool) (out []byte, left []byte, err error) { 216 217 // write key:value pairs to newMap. At then end, 218 // once we know how many pairs we have, then 219 // we can write a map header to out and append 220 // newMap after. 221 // 222 // We don't know the size of {the union 223 // of fields present and fields absent but marked 224 // showZero} until we've scanned the full bts. 225 var newMap []byte 226 227 // get the -1 key out of the map. 228 var n uint32 229 var nbs msgp.NilBitsStack 230 n, bts, err = nbs.ReadMapHeaderBytes(bts) 231 origMapFields := bts 232 if err != nil { 233 panic(err) 234 return nil, nil, err 235 } 236 237 var fnum int 238 var name string 239 foundMinusOne := false 240 241 findMinusOneLoop: 242 for i := uint32(0); i < n; i++ { 243 fnum, bts, err = nbs.ReadIntBytes(bts) 244 if fnum == -1 { 245 name, bts, err = nbs.ReadStringBytes(bts) 246 //fmt.Printf("\n found name = '%#v'\n", name) 247 if err != nil { 248 panic(err) 249 } 250 foundMinusOne = true 251 break findMinusOneLoop 252 } 253 bts, err = msgp.Skip(bts) 254 if err != nil { 255 panic(err) 256 } 257 } 258 259 if !foundMinusOne { 260 if !ignoreMissingStructName { 261 return nil, nil, ErrNoStructNameFound 262 } 263 } 264 265 // INVAR: name is set. lookup the fields. 266 tr, found := sch.Structs[name] 267 if !found { 268 foundMinusOne = false 269 } 270 // tr can be nil if we have no Schema, for example. 271 272 // we might have more fields after adding the 273 // showzero fields. write the new header after 274 // we'vre the struct. 275 numFieldsSeen := 0 276 277 // translate to msgpack2 278 //out = msgp.AppendMapHeader(out, n-1) 279 280 // track found fields, do showzero 281 nextFieldExpected := 0 282 283 // re-read 284 bts = origMapFields 285 for i := uint32(0); i < n; i++ { 286 //p("i = %v", i) 287 fnum, bts, err = nbs.ReadIntBytes(bts) 288 if err != nil { 289 panic(err) 290 } 291 //p("fnum = %v", fnum) 292 if fnum == -1 { 293 bts, err = msgp.Skip(bts) 294 if err != nil { 295 panic(err) 296 } 297 continue 298 } 299 300 if foundMinusOne { 301 302 // PRE: fields must arrive in sorted ascending order, in sequence, 303 // monotonically increasing. 304 newMap, nextFieldExpected, numFieldsSeen = zeroUpTo(tr, fnum, newMap, nextFieldExpected, numFieldsSeen) 305 // encode fnum-> string translation for field name, then the field following 306 newMap = msgp.AppendString(newMap, tr.Fields[fnum].FieldTagName) 307 nextFieldExpected = fnum + 1 308 numFieldsSeen++ 309 } else { 310 // compensate with a fallback when no schema present: 311 // just stringify the zid number so it shows up in the json. 312 newMap = msgp.AppendString(newMap, fmt.Sprintf("%v", fnum)) 313 numFieldsSeen++ 314 } 315 316 // arrays and maps need to be recursively decoded. 317 newMap, bts, err = sch.zebraToMsgp2helper(bts, newMap, ignoreMissingStructName) 318 if err != nil { 319 panic(err) 320 } 321 } 322 323 if foundMinusOne { 324 // done with available fields, are any remaining in the schema? 325 newMap, _, numFieldsSeen = zeroUpTo(tr, len(tr.Fields), newMap, nextFieldExpected, numFieldsSeen) 326 } 327 328 // put a header in front of the newMap pairs... now that we know 329 // how many fields we have seen, so we can. 330 //p("at end of ZebraToMsgp2, numFieldsSeen = %v", numFieldsSeen) 331 out = msgp.AppendMapHeader(out, uint32(numFieldsSeen)) 332 out = append(out, newMap...) 333 334 return out, bts, nil 335 } 336 337 func (sch *Schema) zebraToMsgp2helper(bts []byte, startOut []byte, 338 ignoreMissingStructName bool) (out []byte, left []byte, err error) { 339 340 out = startOut 341 var nbs msgp.NilBitsStack 342 343 k := msgp.NextType(bts) 344 switch k { 345 case msgp.MapType: 346 // recurse 347 var o2 []byte 348 o2, bts, err = sch.ZebraToMsgp2(bts, ignoreMissingStructName) 349 out = append(out, o2...) 350 case msgp.ArrayType: 351 // recurse 352 var sz uint32 353 sz, bts, err = nbs.ReadArrayHeaderBytes(bts) 354 if err != nil { 355 return nil, nil, err 356 } 357 out = msgp.AppendArrayHeader(out, sz) 358 for i := uint32(0); i < sz; i++ { 359 out, bts, err = sch.zebraToMsgp2helper(bts, out, ignoreMissingStructName) 360 if err != nil { 361 return nil, nil, err 362 } 363 } 364 default: 365 // find the end of the next field 366 var end []byte 367 end, err = msgp.Skip(bts) 368 if err != nil { 369 panic(err) 370 } 371 // copy field directly 372 sz := len(bts) - len(end) 373 out = append(out, bts[:sz]...) 374 bts = end 375 } 376 377 return out, bts, nil 378 } 379 380 func writeZeroMsgpValueFor(fld *Field, out []byte) []byte { 381 switch fld.FieldCategory { 382 case BaseElemCat: 383 switch fld.FieldPrimitive { 384 case Invalid: 385 panic("invalid type") 386 case Bytes: 387 return msgp.AppendBytes(out, []byte{}) 388 case String: 389 return msgp.AppendString(out, "") 390 case Float32, Float64, Complex64, Complex128, 391 Uint, Uint8, Uint16, Uint32, Uint64, 392 Byte, Int, Int8, Int16, Int32, Int64: 393 return append(out, 0) 394 case Bool: 395 return msgp.AppendBool(out, false) 396 case Intf: 397 return msgp.AppendNil(out) 398 case Time: 399 return append(out, 0) 400 case Ext: 401 return msgp.AppendNil(out) 402 // IDENT means an unrecognized identifier; 403 // it typically means a named struct type. 404 // The Str field in the Ztype will hold the 405 // name of the struct. 406 case IDENT: 407 return msgp.AppendNil(out) 408 } 409 case MapCat: 410 return msgp.AppendNil(out) 411 case StructCat: 412 return msgp.AppendNil(out) 413 case SliceCat: 414 return msgp.AppendNil(out) 415 case ArrayCat: 416 return msgp.AppendNil(out) 417 case PointerCat: 418 return msgp.AppendNil(out) 419 } 420 return msgp.AppendNil(out) 421 } 422 423 // zeroUpTo() starts from k and stops after stayBelow -1; 424 // it does nothing if k >= stayBelow. Otherwise, for 425 // each field, it handles the ShowZero flag: if 426 // the field is missing and marked ShowZero, then 427 // we write the field name and a zero type to 428 // the msgpack bytes, appending to `out`. 429 // 430 // tr cannot be nil. 431 func zeroUpTo(tr *Struct, stayBelow int, out []byte, k, numFieldsSeen int) (newOut []byte, newNextFieldExpected int, newFieldsSeen int) { 432 //p("zeroUpTo called with k = %v, stayBelow = %v, tr = %#v", k, stayBelow, tr) 433 for k < stayBelow { 434 //p("k is now %v", k) 435 if tr.Fields[k].Skip { 436 //p("skipping field k=%v", k) 437 k++ 438 continue 439 } 440 //p("tr.Fields[k] = '%#v'", tr.Fields[k]) 441 // fill in missing fields that are showzero 442 if tr.Fields[k].ShowZero { 443 numFieldsSeen++ 444 //p("found showzero field at k = %v", k) 445 out = msgp.AppendString(out, tr.Fields[k].FieldTagName) 446 out = writeZeroMsgpValueFor(&(tr.Fields[k]), out) 447 } 448 k++ 449 } 450 return out, k, numFieldsSeen 451 } 452 453 func p(format string, args ...interface{}) { 454 fmt.Printf("\n"+format+"\n", args...) 455 }