// Copyright 2016 Jimmy Zelinskie // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package bencode import ( "bytes" "fmt" "io" "strconv" "time" ) // An Encoder writes bencoded objects to an output stream. type Encoder struct { w io.Writer } // NewEncoder returns a new encoder that writes to w. func NewEncoder(w io.Writer) *Encoder { return &Encoder{w: w} } // Encode writes the bencoding of v to the stream. func (enc *Encoder) Encode(v interface{}) error { return marshal(enc.w, v) } // Marshal returns the bencoding of v. func Marshal(v interface{}) ([]byte, error) { buf := &bytes.Buffer{} err := marshal(buf, v) return buf.Bytes(), err } // Marshaler is the interface implemented by objects that can marshal // themselves. type Marshaler interface { MarshalBencode() ([]byte, error) } // marshal writes types bencoded to an io.Writer func marshal(w io.Writer, data interface{}) error { switch v := data.(type) { case Marshaler: bencoded, err := v.MarshalBencode() if err != nil { return err } _, err = w.Write(bencoded) if err != nil { return err } case string: marshalString(w, v) case int: marshalInt(w, int64(v)) case uint: marshalUint(w, uint64(v)) case int16: marshalInt(w, int64(v)) case uint16: marshalUint(w, uint64(v)) case int32: marshalInt(w, int64(v)) case uint32: marshalUint(w, uint64(v)) case int64: marshalInt(w, v) case uint64: marshalUint(w, v) case []byte: marshalBytes(w, v) case time.Duration: // Assume seconds marshalInt(w, int64(v/time.Second)) case Dict: marshal(w, map[string]interface{}(v)) case []Dict: w.Write([]byte{'l'}) for _, val := range v { err := marshal(w, val) if err != nil { return err } } w.Write([]byte{'e'}) case map[string]interface{}: w.Write([]byte{'d'}) for key, val := range v { marshalString(w, key) err := marshal(w, val) if err != nil { return err } } w.Write([]byte{'e'}) case []string: w.Write([]byte{'l'}) for _, val := range v { err := marshal(w, val) if err != nil { return err } } w.Write([]byte{'e'}) case List: marshal(w, []interface{}(v)) case []interface{}: w.Write([]byte{'l'}) for _, val := range v { err := marshal(w, val) if err != nil { return err } } w.Write([]byte{'e'}) default: return fmt.Errorf("attempted to marshal unsupported type:\n%t", v) } return nil } func marshalInt(w io.Writer, v int64) { w.Write([]byte{'i'}) w.Write([]byte(strconv.FormatInt(v, 10))) w.Write([]byte{'e'}) } func marshalUint(w io.Writer, v uint64) { w.Write([]byte{'i'}) w.Write([]byte(strconv.FormatUint(v, 10))) w.Write([]byte{'e'}) } func marshalBytes(w io.Writer, v []byte) { w.Write([]byte(strconv.Itoa(len(v)))) w.Write([]byte{':'}) w.Write(v) } func marshalString(w io.Writer, v string) { marshalBytes(w, []byte(v)) }