package bencode import "bufio" import "reflect" import "runtime" import "strconv" import "sync" import "sort" func is_empty_value(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() } return false } type encoder struct { *bufio.Writer scratch [64]byte } func (e *encoder) encode(v interface{}) (err error) { defer func() { if e := recover(); e != nil { if _, ok := e.(runtime.Error); ok { panic(e) } err = e.(error) } }() e.reflect_value(reflect.ValueOf(v)) return nil } type string_values []reflect.Value func (sv string_values) Len() int { return len(sv) } func (sv string_values) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } func (sv string_values) Less(i, j int) bool { return sv.get(i) < sv.get(j) } func (sv string_values) get(i int) string { return sv[i].String() } func (e *encoder) reflect_string(s string) { b := strconv.AppendInt(e.scratch[:0], int64(len(s)), 10) e.Write(b) e.WriteString(":") e.WriteString(s) } func (e *encoder) reflect_byte_slice(s []byte) { b := strconv.AppendInt(e.scratch[:0], int64(len(s)), 10) e.Write(b) e.WriteString(":") e.Write(s) } func (e *encoder) reflect_value(v reflect.Value) { if !v.IsValid() { return } switch v.Kind() { case reflect.Bool: if v.Bool() { e.WriteString("i1e") } else { e.WriteString("i0e") } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: b := strconv.AppendInt(e.scratch[:0], v.Int(), 10) e.WriteString("i") e.Write(b) e.WriteString("e") case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10) e.WriteString("i") e.Write(b) e.WriteString("e") case reflect.String: e.reflect_string(v.String()) case reflect.Struct: e.WriteString("d") for _, ef := range encode_fields(v.Type()) { field_value := v.Field(ef.i) if ef.omit_empty && is_empty_value(field_value) { continue } e.reflect_string(ef.tag) e.reflect_value(field_value) } e.WriteString("e") case reflect.Map: if v.Type().Key().Kind() != reflect.String { panic(&MarshalTypeError{v.Type()}) } if v.IsNil() { e.WriteString("de") break } e.WriteString("d") sv := string_values(v.MapKeys()) sort.Sort(sv) for _, key := range sv { e.reflect_string(key.String()) e.reflect_value(v.MapIndex(key)) } e.WriteString("e") case reflect.Slice: if v.IsNil() { e.WriteString("le") break } if v.Type().Elem().Kind() == reflect.Uint8 { s := v.Bytes() e.reflect_byte_slice(s) break } fallthrough case reflect.Array: e.WriteString("l") for i, n := 0, v.Len(); i < n; i++ { e.reflect_value(v.Index(i)) } e.WriteString("e") case reflect.Interface, reflect.Ptr: if v.IsNil() { break } e.reflect_value(v.Elem()) default: panic(&MarshalTypeError{v.Type()}) } } type encode_field struct { i int tag string omit_empty bool } type encode_fields_sort_type []encode_field func (ef encode_fields_sort_type) Len() int { return len(ef) } func (ef encode_fields_sort_type) Swap(i, j int) { ef[i], ef[j] = ef[j], ef[i] } func (ef encode_fields_sort_type) Less(i, j int) bool { return ef[i].tag < ef[j].tag } var ( type_cache_lock sync.RWMutex encode_fields_cache = make(map[reflect.Type][]encode_field) ) func encode_fields(t reflect.Type) []encode_field { type_cache_lock.RLock() fs, ok := encode_fields_cache[t] type_cache_lock.RUnlock() if ok { return fs } type_cache_lock.Lock() defer type_cache_lock.Unlock() fs, ok = encode_fields_cache[t] if ok { return fs } for i, n := 0, t.NumField(); i < n; i++ { f := t.Field(i) if f.PkgPath != "" { continue } if f.Anonymous { continue } var ef encode_field ef.i = i ef.tag = f.Name tv := f.Tag.Get("bencode") if tv != "" { if tv == "-" { continue } name, opts := parse_tag(tv) ef.tag = name ef.omit_empty = opts.contains("omitempty") } fs = append(fs, ef) } fss := encode_fields_sort_type(fs) sort.Sort(fss) encode_fields_cache[t] = fs return fs }