message.go 3.55 KB
Newer Older
1 2 3
package message

import (
4 5
	"io"

6
	blocks "github.com/jbenet/go-ipfs/blocks"
7
	pb "github.com/jbenet/go-ipfs/exchange/bitswap/message/internal/pb"
8
	wantlist "github.com/jbenet/go-ipfs/exchange/bitswap/wantlist"
9
	inet "github.com/jbenet/go-ipfs/net"
10
	u "github.com/jbenet/go-ipfs/util"
11

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12
	ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io"
Jeromy's avatar
Jeromy committed
13
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
14 15
)

16 17 18
// TODO move message.go into the bitswap package
// TODO move bs/msg/internal/pb to bs/internal/pb and rename pb package to bitswap_pb

19
type BitSwapMessage interface {
20 21
	// Wantlist returns a slice of unique keys that represent data wanted by
	// the sender.
Jeromy's avatar
Jeromy committed
22
	Wantlist() []*Entry
23 24

	// Blocks returns a slice of unique blocks
Jeromy's avatar
Jeromy committed
25
	Blocks() []*blocks.Block
26

Jeromy's avatar
Jeromy committed
27
	// AddEntry adds an entry to the Wantlist.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
28 29 30
	AddEntry(key u.Key, priority int)

	Cancel(key u.Key)
Jeromy's avatar
Jeromy committed
31 32 33 34 35

	// Sets whether or not the contained wantlist represents the entire wantlist
	// true = full wantlist
	// false = wantlist 'patch'
	// default: true
36
	SetFull(isFull bool)
Jeromy's avatar
Jeromy committed
37 38

	Full() bool
39

Jeromy's avatar
Jeromy committed
40
	AddBlock(*blocks.Block)
41 42 43 44
	Exportable
}

type Exportable interface {
45
	ToProto() *pb.Message
46
	ToNet(w io.Writer) error
47 48
}

49
type impl struct {
Jeromy's avatar
Jeromy committed
50 51 52
	full     bool
	wantlist map[u.Key]*Entry
	blocks   map[u.Key]*blocks.Block // map to detect duplicates
53 54
}

55
func New() BitSwapMessage {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
56 57 58 59
	return newMsg()
}

func newMsg() *impl {
60
	return &impl{
Jeromy's avatar
Jeromy committed
61 62 63
		blocks:   make(map[u.Key]*blocks.Block),
		wantlist: make(map[u.Key]*Entry),
		full:     true,
64
	}
65 66
}

Jeromy's avatar
Jeromy committed
67
type Entry struct {
68 69
	wantlist.Entry
	Cancel bool
Jeromy's avatar
Jeromy committed
70 71
}

72
func newMessageFromProto(pbm pb.Message) BitSwapMessage {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
73
	m := newMsg()
Jeromy's avatar
Jeromy committed
74 75
	m.SetFull(pbm.GetWantlist().GetFull())
	for _, e := range pbm.GetWantlist().GetEntries() {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
76
		m.addEntry(u.Key(e.GetBlock()), int(e.GetPriority()), e.GetCancel())
77 78
	}
	for _, d := range pbm.GetBlocks() {
79
		b := blocks.NewBlock(d)
Jeromy's avatar
Jeromy committed
80
		m.AddBlock(b)
81
	}
82
	return m
83 84
}

Jeromy's avatar
Jeromy committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98
func (m *impl) SetFull(full bool) {
	m.full = full
}

func (m *impl) Full() bool {
	return m.full
}

func (m *impl) Wantlist() []*Entry {
	var out []*Entry
	for _, e := range m.wantlist {
		out = append(out, e)
	}
	return out
99 100
}

Jeromy's avatar
Jeromy committed
101 102
func (m *impl) Blocks() []*blocks.Block {
	bs := make([]*blocks.Block, 0)
103 104 105 106
	for _, block := range m.blocks {
		bs = append(bs, block)
	}
	return bs
107 108
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
109 110 111 112 113 114 115 116 117
func (m *impl) Cancel(k u.Key) {
	m.addEntry(k, 0, true)
}

func (m *impl) AddEntry(k u.Key, priority int) {
	m.addEntry(k, priority, false)
}

func (m *impl) addEntry(k u.Key, priority int, cancel bool) {
Jeromy's avatar
Jeromy committed
118
	e, exists := m.wantlist[k]
119
	if exists {
Jeromy's avatar
Jeromy committed
120 121 122 123
		e.Priority = priority
		e.Cancel = cancel
	} else {
		m.wantlist[k] = &Entry{
124 125 126 127 128
			Entry: wantlist.Entry{
				Key:      k,
				Priority: priority,
			},
			Cancel: cancel,
Jeromy's avatar
Jeromy committed
129
		}
130
	}
131 132
}

Jeromy's avatar
Jeromy committed
133
func (m *impl) AddBlock(b *blocks.Block) {
134
	m.blocks[b.Key()] = b
135 136
}

137 138 139
func FromNet(r io.Reader) (BitSwapMessage, error) {
	pbr := ggio.NewDelimitedReader(r, inet.MessageSizeMax)

140
	pb := new(pb.Message)
141
	if err := pbr.ReadMsg(pb); err != nil {
142 143
		return nil, err
	}
144

145
	m := newMessageFromProto(*pb)
146
	return m, nil
147 148
}

149
func (m *impl) ToProto() *pb.Message {
Jeromy's avatar
Jeromy committed
150 151 152 153 154 155 156 157
	pbm := new(pb.Message)
	pbm.Wantlist = new(pb.Message_Wantlist)
	for _, e := range m.wantlist {
		pbm.Wantlist.Entries = append(pbm.Wantlist.Entries, &pb.Message_Wantlist_Entry{
			Block:    proto.String(string(e.Key)),
			Priority: proto.Int32(int32(e.Priority)),
			Cancel:   &e.Cancel,
		})
158 159
	}
	for _, b := range m.Blocks() {
Jeromy's avatar
Jeromy committed
160
		pbm.Blocks = append(pbm.Blocks, b.Data)
161
	}
Jeromy's avatar
Jeromy committed
162
	return pbm
163 164
}

165 166 167 168 169 170 171
func (m *impl) ToNet(w io.Writer) error {
	pbw := ggio.NewDelimitedWriter(w)

	if err := pbw.WriteMsg(m.ToProto()); err != nil {
		return err
	}
	return nil
172
}