@@ -139,67 +139,101 @@ func DataSize(data []byte) (uint64, error) {
139
139
}
140
140
}
141
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()`).
143
147
type FSNode struct {
144
- Data []byte
145
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
154
151
}
155
152
156
153
// FSNodeFromBytes unmarshal a protobuf message onto an FSNode.
157
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 )
160
157
if err != nil {
161
158
return nil , err
162
159
}
163
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 ()
169
161
return n , nil
170
162
}
171
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
+
172
186
// AddBlockSize adds the size of the next child block of this node
173
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 )
176
190
}
177
191
178
192
// RemoveBlockSize removes the given child block's size.
179
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 :]... )
182
196
}
183
197
184
198
// GetBytes marshals this node as a protobuf message.
185
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 )
192
201
}
193
202
194
203
// FileSize returns the total size of this tree. That is, the size of
195
204
// the data in this node plus the size of all its children.
196
205
func (n * FSNode ) FileSize () uint64 {
197
- return uint64 ( len ( n . Data )) + n . subtotal
206
+ return n . format . GetFilesize ()
198
207
}
199
208
200
209
// NumChildren returns the number of child blocks of this node
201
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 ()
203
237
}
204
238
205
239
// Metadata is used to store additional FSNode information.
0 commit comments