Skip to content

Commit 6d4a06e

Browse files
committed
allow partial/chunked serialise/deserialise subtree using io.Reader and io.Writer
1 parent 5418b8a commit 6d4a06e

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

subtree_data.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,86 @@ func (s *Data) Serialize() ([]byte, error) {
111111
return buf.Bytes(), nil
112112
}
113113

114+
// WriteTransactionsToWriter writes a range of transactions directly to a writer.
115+
//
116+
// This enables memory-efficient serialization by streaming transactions to disk as they are loaded,
117+
// without requiring all transactions to be in memory simultaneously. Transactions in the specified
118+
// range are written sequentially, skipping any nil entries.
119+
//
120+
// Parameters:
121+
// - w: Writer to stream transactions to
122+
// - startIdx: Starting index (inclusive) of transactions to write
123+
// - endIdx: Ending index (exclusive) of transactions to write
124+
//
125+
// Returns an error if writing fails or if required transactions are missing (nil).
126+
func (s *Data) WriteTransactionsToWriter(w io.Writer, startIdx, endIdx int) error {
127+
if s.Subtree == nil {
128+
return ErrCannotSerializeSubtreeNotSet
129+
}
130+
131+
for i := startIdx; i < endIdx; i++ {
132+
// Skip coinbase placeholder if it's the first transaction
133+
if i == 0 && s.Subtree.Nodes[0].Hash.Equal(*CoinbasePlaceholderHash) {
134+
continue
135+
}
136+
137+
if s.Txs[i] == nil {
138+
return fmt.Errorf("transaction at index %d is nil, cannot serialize", i)
139+
}
140+
141+
// Serialize and stream transaction bytes to writer
142+
txBytes := s.Txs[i].SerializeBytes()
143+
if _, err := w.Write(txBytes); err != nil {
144+
return fmt.Errorf("error writing transaction at index %d: %w", i, err)
145+
}
146+
}
147+
148+
return nil
149+
}
150+
151+
// ReadTransactionsFromReader reads a range of transactions from a reader.
152+
//
153+
// This enables memory-efficient deserialization by reading only a chunk of transactions
154+
// from disk at a time, rather than loading all transactions into memory.
155+
//
156+
// Parameters:
157+
// - r: Reader to read transactions from
158+
// - startIdx: Starting index (inclusive) where transactions should be stored
159+
// - endIdx: Ending index (exclusive) where transactions should be stored
160+
//
161+
// Returns the number of transactions read and any error encountered.
162+
func (s *Data) ReadTransactionsFromReader(r io.Reader, startIdx, endIdx int) (int, error) {
163+
if s.Subtree == nil || len(s.Subtree.Nodes) == 0 {
164+
return 0, ErrSubtreeNodesEmpty
165+
}
166+
167+
txsRead := 0
168+
for i := startIdx; i < endIdx; i++ {
169+
// Skip coinbase placeholder
170+
if i == 0 && s.Subtree.Nodes[0].Hash.Equal(CoinbasePlaceholderHashValue) {
171+
continue
172+
}
173+
174+
tx := &bt.Tx{}
175+
if _, err := tx.ReadFrom(r); err != nil {
176+
if errors.Is(err, io.EOF) {
177+
break
178+
}
179+
return txsRead, fmt.Errorf("error reading transaction at index %d: %w", i, err)
180+
}
181+
182+
// Validate tx hash matches expected
183+
if !s.Subtree.Nodes[i].Hash.Equal(*tx.TxIDChainHash()) {
184+
return txsRead, ErrTxHashMismatch
185+
}
186+
187+
s.Txs[i] = tx
188+
txsRead++
189+
}
190+
191+
return txsRead, nil
192+
}
193+
114194
// serializeFromReader reads transactions from the provided reader and populates the Txs field.
115195
func (s *Data) serializeFromReader(buf io.Reader) error {
116196
var (

0 commit comments

Comments
 (0)