11 // This is a lazy union representing all the possible fields for messages. Go doesn't have ADTs, and
12 // I didn't choose to use type-assertions. Fields are ordered to minimize struct size and padding.
17 ExtendedPayload []byte
19 Index, Begin, Length Integer
24 ExtendedID ExtensionNumber
29 encoding.BinaryUnmarshaler
30 encoding.BinaryMarshaler
33 func MakeCancelMessage(piece, offset, length Integer) Message {
42 func (msg Message) RequestSpec() (ret RequestSpec) {
47 if msg.Type == Piece {
48 return Integer(len(msg.Piece))
56 func (msg Message) MustMarshalBinary() []byte {
57 b, err := msg.MarshalBinary()
64 func (msg Message) MarshalBinary() (data []byte, err error) {
65 // It might look like you could have a pool of buffers and preallocate the message length
66 // prefix, but because we have to return []byte, it becomes non-trivial to make this fast. You
67 // will need a benchmark.
69 mustWrite := func(data any) {
70 err := binary.Write(&buf, binary.BigEndian, data)
75 writeConsecutive := func(data ...any) {
76 for _, d := range data {
81 err = buf.WriteByte(byte(msg.Type))
86 case Choke, Unchoke, Interested, NotInterested, HaveAll, HaveNone:
87 case Have, AllowedFast, Suggest:
88 err = binary.Write(&buf, binary.BigEndian, msg.Index)
89 case Request, Cancel, Reject:
90 for _, i := range []Integer{msg.Index, msg.Begin, msg.Length} {
91 err = binary.Write(&buf, binary.BigEndian, i)
97 _, err = buf.Write(marshalBitfield(msg.Bitfield))
99 for _, i := range []Integer{msg.Index, msg.Begin} {
100 err = binary.Write(&buf, binary.BigEndian, i)
105 n, err := buf.Write(msg.Piece)
109 if n != len(msg.Piece) {
113 err = buf.WriteByte(byte(msg.ExtendedID))
117 _, err = buf.Write(msg.ExtendedPayload)
119 err = binary.Write(&buf, binary.BigEndian, msg.Port)
121 buf.Write(msg.PiecesRoot[:])
122 writeConsecutive(msg.BaseLayer, msg.Index, msg.Length, msg.ProofLayers)
124 err = fmt.Errorf("unknown message type: %v", msg.Type)
127 data = make([]byte, 4+buf.Len())
128 binary.BigEndian.PutUint32(data, uint32(buf.Len()))
129 if buf.Len() != copy(data[4:], buf.Bytes()) {
135 func marshalBitfield(bf []bool) (b []byte) {
136 b = make([]byte, (len(bf)+7)/8)
137 for i, have := range bf {
142 c |= 1 << uint(7-i%8)
148 func (me *Message) UnmarshalBinary(b []byte) error {
150 R: bufio.NewReader(bytes.NewReader(b)),
156 if d.R.Buffered() != 0 {
157 return fmt.Errorf("%d trailing bytes", d.R.Buffered())