Line data Source code
1 : // Copyright 2024 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2 : // of this source code is governed by a BSD-style license that can be found in
3 : // the LICENSE file.
4 :
5 : package colblk
6 :
7 : import (
8 : "bytes"
9 : "context"
10 : "encoding/binary"
11 : "fmt"
12 : "os"
13 : "sync"
14 :
15 : "github.com/cockroachdb/errors"
16 : "github.com/cockroachdb/pebble/internal/base"
17 : "github.com/cockroachdb/pebble/internal/binfmt"
18 : "github.com/cockroachdb/pebble/internal/invariants"
19 : "github.com/cockroachdb/pebble/internal/keyspan"
20 : "github.com/cockroachdb/pebble/internal/treeprinter"
21 : "github.com/cockroachdb/pebble/sstable/block"
22 : )
23 :
24 : // the keyspan header encodes a 32-bit count of the number of unique boundary
25 : // user keys in the block.
26 : const keyspanHeaderSize = 4
27 :
28 : // keyspan block column indexes
29 : const (
30 : // Columns with 1 row per unique boundary user key contained within the
31 : // block (with the count indicated via the keyspan custom block header).
32 : keyspanColBoundaryUserKeys = 0
33 : keyspanColBoundaryKeyIndices = 1
34 : // Columns with 1 row per keyspan.Key (with the count indicated via the
35 : // columnar header's row count).
36 : keyspanColTrailers = 2
37 : keyspanColSuffixes = 3
38 : keyspanColValues = 4
39 : keyspanColumnCount = 5
40 : )
41 :
42 : // A KeyspanBlockWriter writes keyspan blocks. See the colblk package
43 : // documentation for more details on the schema.
44 : type KeyspanBlockWriter struct {
45 : equal base.Equal
46 :
47 : // boundary columns
48 : boundaryUserKeys RawBytesBuilder
49 : boundaryKeyIndexes UintBuilder
50 :
51 : // keyspan.Key columns
52 : trailers UintBuilder
53 : suffixes RawBytesBuilder
54 : values RawBytesBuilder
55 :
56 : enc blockEncoder
57 : keyCount int
58 : unsafeLastUserKey []byte
59 : }
60 :
61 : // Init initializes a keyspan block writer.
62 1 : func (w *KeyspanBlockWriter) Init(equal base.Equal) {
63 1 : w.equal = equal
64 1 : w.boundaryUserKeys.Init()
65 1 : w.boundaryKeyIndexes.Init()
66 1 : w.trailers.Init()
67 1 : w.suffixes.Init()
68 1 : w.values.Init()
69 1 : w.keyCount = 0
70 1 : w.unsafeLastUserKey = nil
71 1 : }
72 :
73 : // Reset resets the keyspan block writer to an empty state, retaining memory for
74 : // reuse.
75 1 : func (w *KeyspanBlockWriter) Reset() {
76 1 : w.boundaryUserKeys.Reset()
77 1 : w.boundaryKeyIndexes.Reset()
78 1 : w.trailers.Reset()
79 1 : w.suffixes.Reset()
80 1 : w.values.Reset()
81 1 : w.enc.reset()
82 1 : w.keyCount = 0
83 1 : w.unsafeLastUserKey = nil
84 1 : }
85 :
86 : // AddSpan appends a new Span to the pending block. Spans must already be
87 : // fragmented (non-overlapping) and added in sorted order.
88 1 : func (w *KeyspanBlockWriter) AddSpan(s keyspan.Span) {
89 1 : // When keyspans are fragmented, abutting spans share a user key. One span's
90 1 : // end key is the next span's start key. Check if the previous user key
91 1 : // equals this span's start key, and avoid encoding it again if so.
92 1 : if w.unsafeLastUserKey == nil || !w.equal(w.unsafeLastUserKey, s.Start) {
93 1 : w.boundaryKeyIndexes.Set(w.boundaryUserKeys.rows, uint64(w.keyCount))
94 1 : w.boundaryUserKeys.Put(s.Start)
95 1 : }
96 : // The end key must be strictly greater than the start key and spans are
97 : // already sorted, so the end key is guaranteed to not be present in the
98 : // column yet. We need to encode it.
99 1 : w.boundaryKeyIndexes.Set(w.boundaryUserKeys.rows, uint64(w.keyCount+len(s.Keys)))
100 1 : w.boundaryUserKeys.Put(s.End)
101 1 :
102 1 : // Hold on to a slice of the copy of s.End we just added to the bytes
103 1 : // builder so that we can compare it to the next span's start key.
104 1 : w.unsafeLastUserKey = w.boundaryUserKeys.data[len(w.boundaryUserKeys.data)-len(s.End):]
105 1 :
106 1 : // Encode each keyspan.Key in the span.
107 1 : for i := range s.Keys {
108 1 : w.trailers.Set(w.keyCount, uint64(s.Keys[i].Trailer))
109 1 : w.suffixes.Put(s.Keys[i].Suffix)
110 1 : w.values.Put(s.Keys[i].Value)
111 1 : w.keyCount++
112 1 : }
113 : }
114 :
115 : // KeyCount returns the count of keyspan.Keys written to the writer.
116 1 : func (w *KeyspanBlockWriter) KeyCount() int {
117 1 : return w.keyCount
118 1 : }
119 :
120 : // UnsafeBoundaryKeys returns the smallest and largest keys written to the
121 : // keyspan block so far. The returned internal keys have user keys that point
122 : // directly into the block writer's memory and must not be mutated.
123 1 : func (w *KeyspanBlockWriter) UnsafeBoundaryKeys() (smallest, largest base.InternalKey) {
124 1 : if w.keyCount == 0 {
125 0 : return smallest, largest
126 0 : }
127 1 : smallest.UserKey = w.boundaryUserKeys.UnsafeGet(0)
128 1 : smallest.Trailer = base.InternalKeyTrailer(w.trailers.Get(0))
129 1 : largest.UserKey = w.boundaryUserKeys.UnsafeGet(w.boundaryUserKeys.rows - 1)
130 1 : largest.Trailer = base.MakeTrailer(base.SeqNumMax,
131 1 : base.InternalKeyTrailer(w.trailers.Get(w.keyCount-1)).Kind())
132 1 : return smallest, largest
133 : }
134 :
135 : // Size returns the size of the pending block.
136 1 : func (w *KeyspanBlockWriter) Size() int {
137 1 : off := blockHeaderSize(keyspanColumnCount, keyspanHeaderSize)
138 1 : // Span boundary columns (with userKeyCount elements).
139 1 : off = w.boundaryUserKeys.Size(w.boundaryUserKeys.rows, off)
140 1 : off = w.boundaryKeyIndexes.Size(w.boundaryUserKeys.rows, off)
141 1 :
142 1 : // keyspan.Key columns (with keyCount elements).
143 1 : off = w.trailers.Size(w.keyCount, off)
144 1 : off = w.suffixes.Size(w.keyCount, off)
145 1 : off = w.values.Size(w.keyCount, off)
146 1 : off++ // trailing padding
147 1 : return int(off)
148 1 : }
149 :
150 : // Finish finalizes the pending block and returns the encoded block.
151 1 : func (w *KeyspanBlockWriter) Finish() []byte {
152 1 : w.enc.init(w.Size(), Header{
153 1 : Version: Version1,
154 1 : Columns: keyspanColumnCount,
155 1 : Rows: uint32(w.keyCount),
156 1 : }, keyspanHeaderSize)
157 1 :
158 1 : // The keyspan block has a 4-byte custom header used to encode the number of
159 1 : // user keys encoded within the user key and start indices columns. All
160 1 : // other columns have the number of rows indicated by the shared columnar
161 1 : // block header.
162 1 : binary.LittleEndian.PutUint32(w.enc.data()[:keyspanHeaderSize], uint32(w.boundaryUserKeys.rows))
163 1 :
164 1 : // Columns with userKeyCount elements.
165 1 : w.enc.encode(w.boundaryUserKeys.rows, &w.boundaryUserKeys)
166 1 : w.enc.encode(w.boundaryUserKeys.rows, &w.boundaryKeyIndexes)
167 1 : // Columns with keyCount elements.
168 1 : w.enc.encode(w.keyCount, &w.trailers)
169 1 : w.enc.encode(w.keyCount, &w.suffixes)
170 1 : w.enc.encode(w.keyCount, &w.values)
171 1 : return w.enc.finish()
172 1 : }
173 :
174 : // String returns a string representation of the pending block's state.
175 1 : func (w *KeyspanBlockWriter) String() string {
176 1 : var buf bytes.Buffer
177 1 : size := uint32(w.Size())
178 1 : fmt.Fprintf(&buf, "size=%d:\n", size)
179 1 :
180 1 : fmt.Fprint(&buf, "0: user keys: ")
181 1 : w.boundaryUserKeys.WriteDebug(&buf, w.boundaryUserKeys.rows)
182 1 : fmt.Fprintln(&buf)
183 1 : fmt.Fprint(&buf, "1: start indices: ")
184 1 : w.boundaryKeyIndexes.WriteDebug(&buf, w.boundaryUserKeys.rows)
185 1 : fmt.Fprintln(&buf)
186 1 :
187 1 : fmt.Fprint(&buf, "2: trailers: ")
188 1 : w.trailers.WriteDebug(&buf, w.keyCount)
189 1 : fmt.Fprintln(&buf)
190 1 : fmt.Fprint(&buf, "3: suffixes: ")
191 1 : w.suffixes.WriteDebug(&buf, w.keyCount)
192 1 : fmt.Fprintln(&buf)
193 1 : fmt.Fprint(&buf, "4: values: ")
194 1 : w.values.WriteDebug(&buf, w.keyCount)
195 1 : fmt.Fprintln(&buf)
196 1 :
197 1 : return buf.String()
198 1 : }
199 :
200 : // A KeyspanReader exposes facilities for reading a keyspan block. A
201 : // KeyspanReader is safe for concurrent use and may be used by multiple
202 : // KeyspanIters concurrently.
203 : type KeyspanReader struct {
204 : blockReader BlockReader
205 : // Span boundary columns with boundaryKeysCount elements.
206 : boundaryKeysCount uint32
207 : boundaryKeys RawBytes
208 : boundaryKeyIndices UnsafeUints
209 :
210 : // keyspan.Key columns with blockReader.header.Rows elements.
211 : trailers UnsafeUints
212 : suffixes RawBytes
213 : values RawBytes
214 : }
215 :
216 : // Init initializes the keyspan reader with the given block data.
217 1 : func (r *KeyspanReader) Init(data []byte) {
218 1 : r.boundaryKeysCount = binary.LittleEndian.Uint32(data[:4])
219 1 : r.blockReader.Init(data, keyspanHeaderSize)
220 1 : // The boundary key columns have a different number of rows than the other
221 1 : // columns, so we call DecodeColumn directly, taking care to pass in
222 1 : // rows=r.boundaryKeysCount.
223 1 : r.boundaryKeys = DecodeColumn(&r.blockReader, keyspanColBoundaryUserKeys,
224 1 : int(r.boundaryKeysCount), DataTypeBytes, DecodeRawBytes)
225 1 : r.boundaryKeyIndices = DecodeColumn(&r.blockReader, keyspanColBoundaryKeyIndices,
226 1 : int(r.boundaryKeysCount), DataTypeUint, DecodeUnsafeUints)
227 1 :
228 1 : r.trailers = r.blockReader.Uints(keyspanColTrailers)
229 1 : r.suffixes = r.blockReader.RawBytes(keyspanColSuffixes)
230 1 : r.values = r.blockReader.RawBytes(keyspanColValues)
231 1 : }
232 :
233 : // DebugString prints a human-readable explanation of the keyspan block's binary
234 : // representation.
235 1 : func (r *KeyspanReader) DebugString() string {
236 1 : f := binfmt.New(r.blockReader.data).LineWidth(20)
237 1 : r.Describe(f)
238 1 : return f.String()
239 1 : }
240 :
241 : // Describe describes the binary format of the keyspan block, assuming
242 : // f.Offset() is positioned at the beginning of the same keyspan block described
243 : // by r.
244 1 : func (r *KeyspanReader) Describe(f *binfmt.Formatter) {
245 1 : // Set the relative offset. When loaded into memory, the beginning of blocks
246 1 : // are aligned. Padding that ensures alignment is done relative to the
247 1 : // current offset. Setting the relative offset ensures that if we're
248 1 : // describing this block within a larger structure (eg, f.Offset()>0), we
249 1 : // compute padding appropriately assuming the current byte f.Offset() is
250 1 : // aligned.
251 1 : f.SetAnchorOffset()
252 1 :
253 1 : f.CommentLine("keyspan block header")
254 1 : f.HexBytesln(4, "user key count: %d", r.boundaryKeysCount)
255 1 : r.blockReader.headerToBinFormatter(f)
256 1 :
257 1 : for i := 0; i < keyspanColumnCount; i++ {
258 1 : // Not all columns in a keyspan block have the same number of rows; the
259 1 : // boundary columns columns are different (and their lengths are held in
260 1 : // the keyspan block header that precedes the ordinary columnar block
261 1 : // header).
262 1 : rows := int(r.blockReader.header.Rows)
263 1 : if i == keyspanColBoundaryUserKeys || i == keyspanColBoundaryKeyIndices {
264 1 : rows = int(r.boundaryKeysCount)
265 1 : }
266 1 : r.blockReader.columnToBinFormatter(f, i, rows)
267 : }
268 1 : f.HexBytesln(1, "block padding byte")
269 : }
270 :
271 : // searchBoundaryKeys returns the index of the first boundary key greater than
272 : // or equal to key and whether or not the key was found exactly.
273 1 : func (r *KeyspanReader) searchBoundaryKeys(cmp base.Compare, key []byte) (index int, equal bool) {
274 1 : i, j := 0, int(r.boundaryKeysCount)
275 1 : for i < j {
276 1 : h := int(uint(i+j) >> 1) // avoid overflow when computing h
277 1 : // i ≤ h < j
278 1 : switch cmp(key, r.boundaryKeys.At(h)) {
279 1 : case +1:
280 1 : i = h + 1
281 1 : case 0:
282 1 : return h, true
283 1 : default:
284 1 : // -1
285 1 : j = h
286 : }
287 : }
288 1 : return i, false
289 : }
290 :
291 : // NewKeyspanIter constructs a new iterator over a keyspan columnar block.
292 : func NewKeyspanIter(
293 : cmp base.Compare, h block.BufferHandle, transforms block.FragmentIterTransforms,
294 1 : ) *KeyspanIter {
295 1 : i := keyspanIterPool.Get().(*KeyspanIter)
296 1 : i.closeCheck = invariants.CloseChecker{}
297 1 : i.handle = h
298 1 : // TODO(jackson): We can teach the block cache to stash a *KeyspanReader.
299 1 : // Then all iters would use the same reader rather than needing to allocate
300 1 : // their own KeyspanReader and parse the high-level block structure
301 1 : // themselves.
302 1 : i.allocReader.Init(h.Get())
303 1 : i.init(cmp, &i.allocReader, transforms)
304 1 : return i
305 1 : }
306 :
307 : var keyspanIterPool = sync.Pool{
308 1 : New: func() interface{} {
309 1 : i := &KeyspanIter{}
310 1 : invariants.SetFinalizer(i, checkKeyspanIter)
311 1 : return i
312 1 : },
313 : }
314 :
315 : // A KeyspanIter is an iterator over a columnar keyspan block. It implements the
316 : // keyspan.FragmentIterator interface.
317 : type KeyspanIter struct {
318 : keyspanIter
319 : handle block.BufferHandle
320 : allocReader KeyspanReader
321 :
322 : closeCheck invariants.CloseChecker
323 : }
324 :
325 : // Close closes the iterator.
326 1 : func (i *KeyspanIter) Close() {
327 1 : i.handle.Release()
328 1 : i.handle = block.BufferHandle{}
329 1 :
330 1 : if invariants.Sometimes(25) {
331 1 : // In invariants mode, sometimes don't add the object to the pool so
332 1 : // that we can check for double closes that take longer than the object
333 1 : // stays in the pool.
334 1 : return
335 1 : }
336 :
337 1 : i.keyspanIter.Close()
338 1 : i.allocReader = KeyspanReader{}
339 1 : i.closeCheck.Close()
340 1 : keyspanIterPool.Put(i)
341 : }
342 :
343 : // A keyspanIter is an iterator over a keyspan block. It implements the
344 : // keyspan.FragmentIterator interface.
345 : type keyspanIter struct {
346 : r *KeyspanReader
347 : cmp base.Compare
348 : transforms block.FragmentIterTransforms
349 : span keyspan.Span
350 : // When positioned, the current span's start key is the user key at
351 : // i.r.userKeys.At(i.startBoundIndex)
352 : // and the current span's end key is the user key at
353 : // i.r.userKeys.At(i.startBoundIndex+1)
354 : startBoundIndex int
355 : keyBuf [2]keyspan.Key
356 : }
357 :
358 : // Assert that KeyspanIter implements the FragmentIterator interface.
359 : var _ keyspan.FragmentIterator = (*keyspanIter)(nil)
360 :
361 : // init initializes the iterator with the given comparison function and keyspan
362 : // reader.
363 : func (i *keyspanIter) init(
364 : cmp base.Compare, r *KeyspanReader, transforms block.FragmentIterTransforms,
365 1 : ) {
366 1 : i.r = r
367 1 : i.cmp = cmp
368 1 : i.transforms = transforms
369 1 : i.span.Start, i.span.End = nil, nil
370 1 : i.startBoundIndex = -1
371 1 : if i.span.Keys == nil {
372 1 : i.span.Keys = i.keyBuf[:0]
373 1 : }
374 : }
375 :
376 : // SeekGE moves the iterator to the first span covering a key greater than
377 : // or equal to the given key. This is equivalent to seeking to the first
378 : // span with an end key greater than the given key.
379 1 : func (i *keyspanIter) SeekGE(key []byte) (*keyspan.Span, error) {
380 1 : // Seek among the boundary keys.
381 1 : j, eq := i.r.searchBoundaryKeys(i.cmp, key)
382 1 : // If the found boundary key does not exactly equal the given key, it's
383 1 : // strictly greater than key. We need to back up one to consider the span
384 1 : // that ends at the this boundary key.
385 1 : if !eq {
386 1 : j = max(j-1, 0)
387 1 : }
388 1 : return i.gatherKeysForward(j), nil
389 : }
390 :
391 : // SeekLT moves the iterator to the last span covering a key less than the
392 : // given key. This is equivalent to seeking to the last span with a start
393 : // key less than the given key.
394 1 : func (i *keyspanIter) SeekLT(key []byte) (*keyspan.Span, error) {
395 1 : // Seek among the boundary keys.
396 1 : j, eq := i.r.searchBoundaryKeys(i.cmp, key)
397 1 : // If eq is true, the found boundary key exactly equals the given key. A
398 1 : // span that starts at exactly [key] does not contain any keys strictly less
399 1 : // than [key], so back up one index.
400 1 : if eq {
401 1 : j--
402 1 : }
403 : // If all boundaries are less than [key], or only the last boundary is
404 : // greater than the key, then we want the last span so we clamp the index to
405 : // the second to last boundary.
406 1 : return i.gatherKeysBackward(min(j, int(i.r.boundaryKeysCount)-2)), nil
407 : }
408 :
409 : // First moves the iterator to the first span.
410 1 : func (i *keyspanIter) First() (*keyspan.Span, error) {
411 1 : return i.gatherKeysForward(0), nil
412 1 : }
413 :
414 : // Last moves the iterator to the last span.
415 1 : func (i *keyspanIter) Last() (*keyspan.Span, error) {
416 1 : return i.gatherKeysBackward(int(i.r.boundaryKeysCount) - 2), nil
417 1 : }
418 :
419 : // Next moves the iterator to the next span.
420 1 : func (i *keyspanIter) Next() (*keyspan.Span, error) {
421 1 : return i.gatherKeysForward(i.startBoundIndex + 1), nil
422 1 : }
423 :
424 : // Prev moves the iterator to the previous span.
425 1 : func (i *keyspanIter) Prev() (*keyspan.Span, error) {
426 1 : return i.gatherKeysBackward(max(i.startBoundIndex-1, -1)), nil
427 1 : }
428 :
429 : // gatherKeysForward returns the first non-empty Span in the forward direction,
430 : // starting with the span formed by using the boundary key at index
431 : // [startBoundIndex] as the span's start boundary.
432 1 : func (i *keyspanIter) gatherKeysForward(startBoundIndex int) *keyspan.Span {
433 1 : if invariants.Enabled && startBoundIndex < 0 {
434 0 : panic(errors.AssertionFailedf("out of bounds: i.startBoundIndex=%d", startBoundIndex))
435 : }
436 1 : i.startBoundIndex = startBoundIndex
437 1 : if i.startBoundIndex >= int(i.r.boundaryKeysCount)-1 {
438 1 : return nil
439 1 : }
440 1 : if !i.isNonemptySpan(i.startBoundIndex) {
441 1 : if i.startBoundIndex == int(i.r.boundaryKeysCount)-2 {
442 0 : // Corruption error
443 0 : panic(base.CorruptionErrorf("keyspan block has empty span at end"))
444 : }
445 1 : i.startBoundIndex++
446 1 : if !i.isNonemptySpan(i.startBoundIndex) {
447 0 : panic(base.CorruptionErrorf("keyspan block has consecutive empty spans"))
448 : }
449 : }
450 1 : return i.materializeSpan()
451 : }
452 :
453 : // gatherKeysBackward returns the first non-empty Span in the backward direction,
454 : // starting with the span formed by using the boundary key at index
455 : // [startBoundIndex] as the span's start boundary.
456 1 : func (i *keyspanIter) gatherKeysBackward(startBoundIndex int) *keyspan.Span {
457 1 : i.startBoundIndex = startBoundIndex
458 1 : if i.startBoundIndex < 0 {
459 1 : return nil
460 1 : }
461 1 : if invariants.Enabled && i.startBoundIndex >= int(i.r.boundaryKeysCount)-1 {
462 0 : panic(errors.AssertionFailedf("out of bounds: i.startBoundIndex=%d, i.r.boundaryKeysCount=%d",
463 0 : i.startBoundIndex, i.r.boundaryKeysCount))
464 : }
465 1 : if !i.isNonemptySpan(i.startBoundIndex) {
466 1 : if i.startBoundIndex == 0 {
467 0 : // Corruption error
468 0 : panic(base.CorruptionErrorf("keyspan block has empty span at beginning"))
469 : }
470 1 : i.startBoundIndex--
471 1 : if !i.isNonemptySpan(i.startBoundIndex) {
472 0 : panic(base.CorruptionErrorf("keyspan block has consecutive empty spans"))
473 : }
474 : }
475 1 : return i.materializeSpan()
476 : }
477 :
478 : // isNonemptySpan returns true if the span starting at i.startBoundIndex
479 : // contains keys.
480 1 : func (i *keyspanIter) isNonemptySpan(startBoundIndex int) bool {
481 1 : return i.r.boundaryKeyIndices.At(startBoundIndex) < i.r.boundaryKeyIndices.At(startBoundIndex+1)
482 1 : }
483 :
484 : // materializeSpan constructs the current span from i.startBoundIndex and
485 : // i.{start,end}KeyIndex.
486 1 : func (i *keyspanIter) materializeSpan() *keyspan.Span {
487 1 : // TODO(jackson): Apply i.transforms.
488 1 :
489 1 : i.span = keyspan.Span{
490 1 : Start: i.r.boundaryKeys.At(i.startBoundIndex),
491 1 : End: i.r.boundaryKeys.At(i.startBoundIndex + 1),
492 1 : Keys: i.span.Keys[:0],
493 1 : }
494 1 : startIndex := i.r.boundaryKeyIndices.At(i.startBoundIndex)
495 1 : endIndex := i.r.boundaryKeyIndices.At(i.startBoundIndex + 1)
496 1 : if cap(i.span.Keys) < int(endIndex-startIndex) {
497 1 : i.span.Keys = make([]keyspan.Key, 0, int(endIndex-startIndex))
498 1 : }
499 1 : for j := startIndex; j < endIndex; j++ {
500 1 : i.span.Keys = append(i.span.Keys, keyspan.Key{
501 1 : Trailer: base.InternalKeyTrailer(i.r.trailers.At(int(j))),
502 1 : Suffix: i.r.suffixes.At(int(j)),
503 1 : Value: i.r.values.At(int(j)),
504 1 : })
505 1 : }
506 1 : return &i.span
507 : }
508 :
509 : // Close closes the iterator.
510 1 : func (i *keyspanIter) Close() {
511 1 : *i = keyspanIter{}
512 1 : }
513 :
514 : // SetContext implements keyspan.FragmentIterator.
515 0 : func (i *keyspanIter) SetContext(context.Context) {}
516 :
517 : // WrapChildren implements keyspan.FragmentIterator.
518 0 : func (i *keyspanIter) WrapChildren(keyspan.WrapFn) {}
519 :
520 : // DebugTree is part of the FragmentIterator interface.
521 0 : func (i *keyspanIter) DebugTree(tp treeprinter.Node) {
522 0 : tp.Childf("%T(%p)", i, i)
523 0 : }
524 :
525 1 : func checkKeyspanIter(obj interface{}) {
526 1 : i := obj.(*KeyspanIter)
527 1 : if p := i.handle.Get(); p != nil {
528 0 : fmt.Fprintf(os.Stderr, "KeyspanIter.handle is not nil: %p\n", p)
529 0 : os.Exit(1)
530 0 : }
531 : }
|