Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions go/shard/shard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package shard
import (
"crypto/sha512"
"fmt"
"io"
"io/ioutil"
"os"
"path"
Expand All @@ -14,7 +15,7 @@ func ExampleWriter() {
fmt.Println(err)
return
}
defer os.RemoveAll(name) //nolint:wsl
defer func() { _ = os.RemoveAll(name) }() //nolint:wsl

w := NewWriter(5, &PrefixSum64Hash{sha512.New()}, NewOSFileWriterFactory(path.Join(name, "test-")))
records := []string{
Expand All @@ -35,7 +36,7 @@ func ExampleWriter() {
}
}

w.Close()
_ = w.Close() // Changed this line

for i := 0; i < 5; i++ {
filename := fmt.Sprintf("test-%05d-of-00005", i)
Expand All @@ -49,3 +50,53 @@ func ExampleWriter() {
// test-00003-of-00005:
// test-00004-of-00005:test1test2test3test1
}

func ExamplePrefixSum64Hash_Sum64() {
hasher := &PrefixSum64Hash{sha512.New()}
hasher.Write([]byte("hello"))
fmt.Println(hasher.Sum64())
// Output: 11200964803485168504
}

func ExampleNewOSFileWriterFactory() {
tempDir, err := ioutil.TempDir("", "examplefactory")
Copy link

Copilot AI Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ioutil package is deprecated; consider using os.MkdirTemp to create a temporary directory in newer Go versions.

Copilot uses AI. Check for mistakes.
if err != nil {
fmt.Println("Failed to create temp dir:", err)
return
}
defer func() { _ = os.RemoveAll(tempDir) }()

factoryPrefix := "testfactory-"
factory := NewOSFileWriterFactory(path.Join(tempDir, factoryPrefix))

numFiles := 3
for i := 0; i < numFiles; i++ {
writer := factory(i, numFiles)
_, err := fmt.Fprintf(writer, "data for file %d", i)
if err != nil {
fmt.Printf("Error writing to file %d: %v\n", i, err)
}
if closer, ok := writer.(io.Closer); ok {
err := closer.Close()
if err != nil {
fmt.Printf("Error closing file %d: %v\n", i, err)
}
}
}

for i := 0; i < numFiles; i++ {
fileName := fmt.Sprintf("%s%05d-of-%05d", factoryPrefix, i, numFiles)
filePath := path.Join(tempDir, fileName)
content, err := ioutil.ReadFile(filePath)
Copy link

Copilot AI Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since ioutil is deprecated, consider replacing ioutil.ReadFile with os.ReadFile.

Suggested change
content, err := ioutil.ReadFile(filePath)
content, err := os.ReadFile(filePath)

Copilot uses AI. Check for mistakes.
if err != nil {
fmt.Printf("Error reading file %s: %v\n", fileName, err)
continue
}
fmt.Printf("%s:%s\n", fileName, string(content))
}

// Output:
// testfactory-00000-of-00003:data for file 0
// testfactory-00001-of-00003:data for file 1
// testfactory-00002-of-00003:data for file 2
}
29 changes: 29 additions & 0 deletions go/sort/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sort
import (
"container/heap"
"fmt"
"sort"

"github.com/jaeyeom/sstable/go/sstable"
)
Expand Down Expand Up @@ -41,3 +42,31 @@ func Example_heap() {
// {{[107 101 121 51] [118 97 108 117 101]} 1}
// {{[107 101 121 52] [118 97 108 117 101]} 3}
}

func ExampleEntries_sort() {
// Create a slice of HeapEntry.
// sstable.Entry has Key and Value as []byte.
data := Entries{
{Entry: sstable.Entry{Key: []byte("key3"), Value: []byte("valueA")}},
{Entry: sstable.Entry{Key: []byte("key1"), Value: []byte("valueB")}},
{Entry: sstable.Entry{Key: []byte("key2"), Value: []byte("valueD")}},
{Entry: sstable.Entry{Key: []byte("key1"), Value: []byte("valueC")}},
}

// Call sort.Sort on the Entries instance.
// This uses the Len, Less, Swap methods defined on Entries.
sort.Sort(data)

// Iterate through the sorted Entries and print.
for _, heapEntry := range data {
fmt.Printf("%s %s\n", string(heapEntry.Entry.Key), string(heapEntry.Entry.Value))
}

// Define the expected output.
// Sorted first by key, then by value for identical keys.
// Output:
// key1 valueB
// key1 valueC
// key2 valueD
// key3 valueA
}
71 changes: 71 additions & 0 deletions go/sstable/cursor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package sstable

import (
"bytes"
"fmt"
// "io" // Will add if ReadEntry or other functions require it directly for type matching.
// For now, bytes.Reader and the CursorToOffset itself will handle reader interfaces.
)

func ExampleCursorToOffset() {
// 1. Prepare entries
entry1 := Entry{Key: []byte("key1"), Value: []byte("value1")}
entry2 := Entry{Key: []byte("key2"), Value: []byte("value22")} // Different length value

// 2. Marshal entries into a buffer
var buf bytes.Buffer
data1, err := entry1.MarshalBinary()
if err != nil {
fmt.Println("Error marshalling entry1:", err)
return
}
buf.Write(data1)

data2, err := entry2.MarshalBinary()
if err != nil {
fmt.Println("Error marshalling entry2:", err)
return
}
buf.Write(data2)

serializedData := buf.Bytes()

// 3. Calculate endOffset (total size of serialized data)
// This could also be entry1.Size() + entry2.Size()
endOffset := uint64(len(serializedData))

// 4. Create a bytes.NewReader
reader := bytes.NewReader(serializedData)

// 5. Create CursorToOffset instance
// Reader can be io.Reader or io.ReaderAt. bytes.NewReader implements both.
// For this example, let's ensure it's treated as a general io.Reader
// by the cursor, though CursorToOffset will detect it as io.ReaderAt if not type asserted.
// The implementation of CursorToOffset.Entry() prefers io.ReaderAt if available.
cursor := &CursorToOffset{
reader: reader, // bytes.NewReader is also an io.ReaderAt
offset: 0,
endOffset: endOffset,
entry: nil, // Starts with no entry loaded
}

// 6. Loop through entries
for !cursor.Done() {
currentEntry := cursor.Entry()
if currentEntry == nil {
// This might happen if ReadEntry/ReadEntryAt fails,
// or if Done() condition is met but loop condition was already checked.
// Or if endOffset is 0.
// Given the Done() logic, if Entry() returns nil, Done() should usually be true.
// Let's assume valid entries for example purposes.
fmt.Println("Error: current entry is nil, but not done.")
break
}
fmt.Printf("Key: %s, Value: %s\n", string(currentEntry.Key), string(currentEntry.Value))
cursor.Next()
}

// Output:
// Key: key1, Value: value1
// Key: key2, Value: value22
}
40 changes: 40 additions & 0 deletions go/sstable/entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,46 @@ func ExampleReadEntry() {
// &{[1 2 3] [5 6 7 8]}
}

func ExampleEntry_WriteTo() {
e := Entry{
Key: []byte{1, 2}, // Key length 2
Value: []byte{3, 4, 5}, // Value length 3
}

var buf bytes.Buffer
_, err := e.WriteTo(&buf)
if err != nil {
fmt.Println("Error:", err)
return
}

// Optionally print n, but the main thing is the buffer content
// fmt.Printf("Bytes written: %d\n", n)
// For example consistency, usually only the primary output is shown.

fmt.Println(buf.Bytes())
// Expected format: [0 0 0 keyLen 0 0 0 valLen keyBytes valBytes]
// KeyLen = 2 -> [0 0 0 2]
// ValLen = 3 -> [0 0 0 3]
// Key = [1 2]
// Value = [3 4 5]
// Output:
// [0 0 0 2 0 0 0 3 1 2 3 4 5]
}

func ExampleEntry_Size() {
e := Entry{
Key: []byte("test"), // length 4
Value: []byte("example"), // length 7
}

size := e.Size()
fmt.Println(size)
// Expected: 8 (for length fields) + 4 (len("test")) + 7 (len("example")) = 19
// Output:
// 19
}

func ExampleReadEntryAt() {
f := bytes.NewReader([]byte{0, 0, 0, 3, 0, 0, 0, 4, 1, 2, 3, 5, 6, 7, 8})
e, _ := ReadEntryAt(f, 0)
Expand Down
4 changes: 2 additions & 2 deletions go/sstable/header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

//nolint:govet
func ExampleHeaderMarshalBinary() {
func Example_headerMarshalBinary() {
h := header{
version: 1,
numBlocks: 2,
Expand All @@ -18,7 +18,7 @@ func ExampleHeaderMarshalBinary() {
}

//nolint:govet
func ExampleHeaderUnmarshalBinary() {
func Example_headerUnmarshalBinary() {
h := header{}

err := h.UnmarshalBinary([]byte{
Expand Down
14 changes: 7 additions & 7 deletions go/sstable/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

//nolint:govet
func ExampleIndexBufferWrite() {
func Example_indexBufferWrite() {
w := &indexBuffer{
maxBlockLength: 64 * 1024,
}
Expand All @@ -20,7 +20,7 @@ func ExampleIndexBufferWrite() {
}

//nolint:govet
func ExampleIndexEntryIndexOf() {
func Example_indexEntryIndexOf() {
i := &index{
{0, 60023, []byte{1, 2, 3}},
{60023, 30011, []byte{2, 3, 4}},
Expand All @@ -39,7 +39,7 @@ func ExampleIndexEntryIndexOf() {
}

//nolint:govet
func ExampleIndexReadFrom() {
func Example_indexReadFrom() {
var i index

buf := bytes.NewBuffer([]byte{
Expand All @@ -56,7 +56,7 @@ func ExampleIndexReadFrom() {
}

//nolint:govet
func ExampleIndexReadAt() {
func Example_indexReadAt() {
var i index

f := bytes.NewReader([]byte{
Expand All @@ -73,7 +73,7 @@ func ExampleIndexReadAt() {
}

//nolint:govet
func ExampleIndexWriteTo() {
func Example_indexWriteTo() {
i := &index{
{0, 60023, []byte{1, 2, 3}},
{60023, 30011, []byte{2, 3, 4}},
Expand All @@ -92,7 +92,7 @@ func ExampleIndexWriteTo() {
}

//nolint:govet
func ExampleIndexEntryMarshalBinary() {
func Example_indexEntryMarshalBinary() {
b := indexEntry{
blockOffset: 1,
blockLength: 10,
Expand All @@ -110,7 +110,7 @@ func ExampleIndexEntryMarshalBinary() {
}

//nolint:govet
func ExampleIndexEntryUnmarshalBinary() {
func Example_indexEntryUnmarshalBinary() {
var b indexEntry

err := b.UnmarshalBinary([]byte{0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 5, 6, 7})
Expand Down
57 changes: 57 additions & 0 deletions go/sstable/recordio_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package sstable

import (
"bytes"
"fmt"
)

func ExampleNewRecordIOReader() {
// 1. Prepare entries
entry1 := Entry{Key: []byte("keyA"), Value: []byte("valueA")}
entry2 := Entry{Key: []byte("keyB"), Value: []byte("valueBB")} // Different length value

// 2. Marshal entries into a buffer
var dataBuf bytes.Buffer
data1Bytes, err := entry1.MarshalBinary()
if err != nil {
fmt.Println("Error marshalling entry1:", err)
return
}
dataBuf.Write(data1Bytes)

data2Bytes, err := entry2.MarshalBinary()
if err != nil {
fmt.Println("Error marshalling entry2:", err)
return
}
dataBuf.Write(data2Bytes)

serializedEntries := dataBuf.Bytes()

// 3. Create a bytes.NewReader (which implements io.Reader and io.ReaderAt)
reader := bytes.NewReader(serializedEntries)

// 4. Calculate the total size of the serialized data
totalSize := uint64(len(serializedEntries))

// 5. Call NewRecordIOReader
// NewRecordIOReader takes an io.Reader, but CursorToOffset (which it returns)
// will try to use it as io.ReaderAt if possible. bytes.NewReader supports this.
cursor := NewRecordIOReader(reader, totalSize)

// 6. Loop through entries using the cursor
for !cursor.Done() {
currentEntry := cursor.Entry()
if currentEntry == nil {
// Should not happen in this example if data is valid and size is correct
fmt.Println("Error: current entry is nil, but not done.")
break
}
fmt.Printf("Key: %s, Value: %s\n", string(currentEntry.Key), string(currentEntry.Value))
cursor.Next()
}

// Output:
// Key: keyA, Value: valueA
// Key: keyB, Value: valueBB
}