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  }