github.com/go-enjin/golang-org-x-text@v0.12.1-enjin.2/internal/gen/bitfield/bitfield.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package bitfield converts annotated structs into integer values. 6 // 7 // Any field that is marked with a bitfield tag is compacted. The tag value has 8 // two parts. The part before the comma determines the method name for a 9 // generated type. If left blank the name of the field is used. 10 // The part after the comma determines the number of bits to use for the 11 // representation. 12 package bitfield 13 14 import ( 15 "bytes" 16 "fmt" 17 "io" 18 "reflect" 19 "strconv" 20 "strings" 21 ) 22 23 // Config determines settings for packing and generation. If a Config is used, 24 // the same Config should be used for packing and generation. 25 type Config struct { 26 // NumBits fixes the maximum allowed bits for the integer representation. 27 // If NumBits is not 8, 16, 32, or 64, the actual underlying integer size 28 // will be the next largest available. 29 NumBits uint 30 31 // If Package is set, code generation will write a package clause. 32 Package string 33 34 // TypeName is the name for the generated type. By default it is the name 35 // of the type of the value passed to Gen. 36 TypeName string 37 } 38 39 var nullConfig = &Config{} 40 41 // Pack packs annotated bit ranges of struct x in an integer. 42 // 43 // Only fields that have a "bitfield" tag are compacted. 44 func Pack(x interface{}, c *Config) (packed uint64, err error) { 45 packed, _, err = pack(x, c) 46 return 47 } 48 49 func pack(x interface{}, c *Config) (packed uint64, nBit uint, err error) { 50 if c == nil { 51 c = nullConfig 52 } 53 nBits := c.NumBits 54 v := reflect.ValueOf(x) 55 v = reflect.Indirect(v) 56 t := v.Type() 57 pos := 64 - nBits 58 if nBits == 0 { 59 pos = 0 60 } 61 for i := 0; i < v.NumField(); i++ { 62 v := v.Field(i) 63 field := t.Field(i) 64 f, err := parseField(field) 65 66 if err != nil { 67 return 0, 0, err 68 } 69 if f.nBits == 0 { 70 continue 71 } 72 value := uint64(0) 73 switch v.Kind() { 74 case reflect.Bool: 75 if v.Bool() { 76 value = 1 77 } 78 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 79 value = v.Uint() 80 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 81 x := v.Int() 82 if x < 0 { 83 return 0, 0, fmt.Errorf("bitfield: negative value for field %q not allowed", field.Name) 84 } 85 value = uint64(x) 86 } 87 if value > (1<<f.nBits)-1 { 88 return 0, 0, fmt.Errorf("bitfield: value %#x of field %q does not fit in %d bits", value, field.Name, f.nBits) 89 } 90 shift := 64 - pos - f.nBits 91 if pos += f.nBits; pos > 64 { 92 return 0, 0, fmt.Errorf("bitfield: no more bits left for field %q", field.Name) 93 } 94 packed |= value << shift 95 } 96 if nBits == 0 { 97 nBits = posToBits(pos) 98 packed >>= (64 - nBits) 99 } 100 return packed, nBits, nil 101 } 102 103 type field struct { 104 name string 105 value uint64 106 nBits uint 107 } 108 109 // parseField parses a tag of the form [<name>][:<nBits>][,<pos>[..<end>]] 110 func parseField(field reflect.StructField) (f field, err error) { 111 s, ok := field.Tag.Lookup("bitfield") 112 if !ok { 113 return f, nil 114 } 115 switch field.Type.Kind() { 116 case reflect.Bool: 117 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 118 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 119 default: 120 return f, fmt.Errorf("bitfield: field %q is not an integer or bool type", field.Name) 121 } 122 bits := s 123 f.name = "" 124 125 if i := strings.IndexByte(s, ','); i >= 0 { 126 bits = s[:i] 127 f.name = s[i+1:] 128 } 129 if bits != "" { 130 nBits, err := strconv.ParseUint(bits, 10, 8) 131 if err != nil { 132 return f, fmt.Errorf("bitfield: invalid bit size for field %q: %v", field.Name, err) 133 } 134 f.nBits = uint(nBits) 135 } 136 if f.nBits == 0 { 137 if field.Type.Kind() == reflect.Bool { 138 f.nBits = 1 139 } else { 140 f.nBits = uint(field.Type.Bits()) 141 } 142 } 143 if f.name == "" { 144 f.name = field.Name 145 } 146 return f, err 147 } 148 149 func posToBits(pos uint) (bits uint) { 150 switch { 151 case pos <= 8: 152 bits = 8 153 case pos <= 16: 154 bits = 16 155 case pos <= 32: 156 bits = 32 157 case pos <= 64: 158 bits = 64 159 default: 160 panic("unreachable") 161 } 162 return bits 163 } 164 165 // Gen generates code for unpacking integers created with Pack. 166 func Gen(w io.Writer, x interface{}, c *Config) error { 167 if c == nil { 168 c = nullConfig 169 } 170 _, nBits, err := pack(x, c) 171 if err != nil { 172 return err 173 } 174 175 t := reflect.TypeOf(x) 176 if t.Kind() == reflect.Ptr { 177 t = t.Elem() 178 } 179 if c.TypeName == "" { 180 c.TypeName = t.Name() 181 } 182 firstChar := []rune(c.TypeName)[0] 183 184 buf := &bytes.Buffer{} 185 186 print := func(w io.Writer, format string, args ...interface{}) { 187 if _, e := fmt.Fprintf(w, format+"\n", args...); e != nil && err == nil { 188 err = fmt.Errorf("bitfield: write failed: %v", err) 189 } 190 } 191 192 pos := uint(0) 193 for i := 0; i < t.NumField(); i++ { 194 field := t.Field(i) 195 f, _ := parseField(field) 196 if f.nBits == 0 { 197 continue 198 } 199 shift := nBits - pos - f.nBits 200 pos += f.nBits 201 202 retType := field.Type.Name() 203 print(buf, "\nfunc (%c %s) %s() %s {", firstChar, c.TypeName, f.name, retType) 204 if field.Type.Kind() == reflect.Bool { 205 print(buf, "\tconst bit = 1 << %d", shift) 206 print(buf, "\treturn %c&bit == bit", firstChar) 207 } else { 208 print(buf, "\treturn %s((%c >> %d) & %#x)", retType, firstChar, shift, (1<<f.nBits)-1) 209 } 210 print(buf, "}") 211 } 212 213 if c.Package != "" { 214 print(w, "// Code generated by github.com/go-enjin/golang-org-x-text/internal/gen/bitfield. DO NOT EDIT.\n") 215 print(w, "package %s\n", c.Package) 216 } 217 218 bits := posToBits(pos) 219 220 print(w, "type %s uint%d", c.TypeName, bits) 221 222 if _, err := io.Copy(w, buf); err != nil { 223 return fmt.Errorf("bitfield: write failed: %v", err) 224 } 225 return nil 226 }