139
}
140
}
141
142
-// An FSNode represents a filesystem object.
142
+// An FSNode represents a filesystem object using the UnixFS specification.
143
+//
144
+// The `NewFSNode` constructor should be used instead of just calling `new(FSNode)`
145
+// to guarantee that the required (`Type` and `Filesize`) fields in the `format`
146
+// structure are initialized before marshaling (in `GetBytes()`).
147
type FSNode struct {
144
- Data []byte
148
146
- // total data size for each child
147
- blocksizes []uint64
148
-
149
- // running sum of blocksizes
150
- subtotal uint64
151
-
152
- // node type of this node
153
- Type pb.Data_DataType
149
+ // UnixFS format defined as a protocol buffers message.
150
+ format pb.Data
151
}
152
153
// FSNodeFromBytes unmarshal a protobuf message onto an FSNode.
154
func FSNodeFromBytes(b []byte) (*FSNode, error) {
158
- pbn := new(pb.Data)
159
- err := proto.Unmarshal(b, pbn)
155
+ n := new(FSNode)
156
+ err := proto.Unmarshal(b, &n.format)
157
if err != nil {
158
return nil, err
159
}
160
164
- n := new(FSNode)
165
- n.Data = pbn.Data
166
- n.blocksizes = pbn.Blocksizes
167
- n.subtotal = pbn.GetFilesize() - uint64(len(n.Data))
168
- n.Type = pbn.GetType()
161
return n, nil
162
}
163
164
+// NewFSNode creates a new FSNode structure with the given `dataType`.
165
+//
166
+// It initializes the (required) `Type` field (that doesn't have a `Set()`
167
+// accessor so it must be specified at creation), otherwise the `Marshal()`
168
+// method in `GetBytes()` would fail (`required field "Type" not set`).
169
+//
170
+// It also initializes the `Filesize` pointer field to ensure its value
171
+// is never nil before marshaling, this is not a required field but it is
172
+// done to be backwards compatible with previous `go-ipfs` versions hash.
173
+// (If it wasn't initialized there could be cases where `Filesize` could
174
+// have been left at nil, when the `FSNode` was created but no data or
175
+// child nodes were set to adjust it, as is the case in `NewLeaf()`.)
176
+func NewFSNode(dataType pb.Data_DataType) *FSNode {
177
+ n := new(FSNode)
178
+ n.format.Type = &dataType
179
+
180
+ // Initialize by `Filesize` by updating it with a dummy (zero) value.
181
+ n.UpdateFilesize(0)
182
+
183
+ return n
184
+}
185
+
186
// AddBlockSize adds the size of the next child block of this node
187
func (n *FSNode) AddBlockSize(s uint64) {
174
- n.subtotal += s
175
- n.blocksizes = append(n.blocksizes, s)
188
+ n.UpdateFilesize(int64(s))
189
+ n.format.Blocksizes = append(n.format.Blocksizes, s)
190
}
191
192
// RemoveBlockSize removes the given child block's size.
193
func (n *FSNode) RemoveBlockSize(i int) {
180
- n.subtotal -= n.blocksizes[i]
181
- n.blocksizes = append(n.blocksizes[:i], n.blocksizes[i+1:]...)
194
+ n.UpdateFilesize(-int64(n.format.Blocksizes[i]))
195
+ n.format.Blocksizes = append(n.format.Blocksizes[:i], n.format.Blocksizes[i+1:]...)
196
}
197
198
// GetBytes marshals this node as a protobuf message.
199
func (n *FSNode) GetBytes() ([]byte, error) {
186
- pbn := new(pb.Data)
187
- pbn.Type = &n.Type
188
- pbn.Filesize = proto.Uint64(uint64(len(n.Data)) + n.subtotal)
189
- pbn.Blocksizes = n.blocksizes
190
- pbn.Data = n.Data
191
- return proto.Marshal(pbn)
200
+ return proto.Marshal(&n.format)
201
}
202
203
// FileSize returns the total size of this tree. That is, the size of
204
// the data in this node plus the size of all its children.
205
func (n *FSNode) FileSize() uint64 {
197
- return uint64(len(n.Data)) + n.subtotal
206
+ return n.format.GetFilesize()
207
}
208
209
// NumChildren returns the number of child blocks of this node
210
func (n *FSNode) NumChildren() int {
202
- return len(n.blocksizes)
211
+ return len(n.format.Blocksizes)
212
+}
213
+
214
+// GetData retrieves the `Data` field from the internal `format`.
215
+func (n *FSNode) GetData() []byte {
216
+ return n.format.GetData()
217
+}
218
+
219
+// SetData sets the `Data` field from the internal `format`
220
+// updating its `Filesize`.
221
+func (n *FSNode) SetData(newData []byte) {
222
+ n.UpdateFilesize(int64(len(newData) - len(n.GetData())))
223
+ n.format.Data = newData
224
+}
225
+
226
+// UpdateFilesize updates the `Filesize` field from the internal `format`
227
+// by a signed difference (`filesizeDiff`).
228
+// TODO: Add assert to check for `Filesize` > 0?
229
+func (n *FSNode) UpdateFilesize(filesizeDiff int64) {
230
+ n.format.Filesize = proto.Uint64(uint64(
231
+ int64(n.format.GetFilesize()) + filesizeDiff))
232
+}
233
+
234
+// GetType retrieves the `Type` field from the internal `format`.
235
+func (n *FSNode) GetType() pb.Data_DataType {
236
+ return n.format.GetType()
237
}
238
239
// Metadata is used to store additional FSNode information.