updates.go
1package ygo
2
3/*
4#include "libyrs.h"
5*/
6import "C"
7import (
8 "fmt"
9 "unsafe"
10)
11
12// Update represents a binary update that can be applied to remote documents.
13type Update struct {
14 data []byte
15}
16
17// UpdateFromBytes creates an Update from raw binary data.
18// This is useful when loading a document from a file.
19func UpdateFromBytes(data []byte) *Update {
20 if len(data) == 0 {
21 return nil
22 }
23 // Make a copy to ensure the data remains valid
24 dataCopy := make([]byte, len(data))
25 copy(dataCopy, data)
26 return &Update{data: dataCopy}
27}
28
29// Data returns the binary update data.
30func (u *Update) Data() []byte {
31 return u.data
32}
33
34// StateVector represents the state of a document for calculating deltas.
35type StateVector struct {
36 data []byte
37}
38
39// NewStateVectorFromBytes creates a StateVector from raw binary data.
40func NewStateVectorFromBytes(data []byte) *StateVector {
41 if len(data) == 0 {
42 return nil
43 }
44 dataCopy := make([]byte, len(data))
45 copy(dataCopy, data)
46 return &StateVector{data: dataCopy}
47}
48
49// Data returns the binary state vector.
50func (sv *StateVector) Data() []byte {
51 return sv.data
52}
53
54// GetStateVector returns the current state vector of the document.
55// This can be sent to remote peers to calculate what updates they need.
56func (txn *Transaction) GetStateVector() *StateVector {
57 if txn.ptr == nil {
58 return nil
59 }
60 var length C.uint32_t
61 ptr := C.ytransaction_state_vector_v1(txn.ptr, &length)
62 if ptr == nil {
63 return nil
64 }
65 defer C.ybinary_destroy(ptr, length)
66
67 data := make([]byte, int(length))
68 copy(data, (*[1 << 30]byte)(unsafe.Pointer(ptr))[:length:length])
69 return &StateVector{data: data}
70}
71
72// GetStateDiff returns a delta update based on a remote state vector.
73// If remoteSV is nil, returns a full state snapshot.
74func (txn *Transaction) GetStateDiff(remoteSV *StateVector) *Update {
75 if txn.ptr == nil {
76 return nil
77 }
78
79 var length C.uint32_t
80 var ptr *C.char
81
82 if remoteSV == nil || len(remoteSV.data) == 0 {
83 ptr = C.ytransaction_state_diff_v1(txn.ptr, nil, 0, &length)
84 } else {
85 ptr = C.ytransaction_state_diff_v1(txn.ptr,
86 (*C.char)(unsafe.Pointer(&remoteSV.data[0])),
87 C.uint32_t(len(remoteSV.data)), &length)
88 }
89
90 if ptr == nil {
91 return nil
92 }
93 defer C.ybinary_destroy(ptr, length)
94
95 data := make([]byte, int(length))
96 copy(data, (*[1 << 30]byte)(unsafe.Pointer(ptr))[:length:length])
97 return &Update{data: data}
98}
99
100// ApplyUpdate applies a binary update to the document.
101// Returns an error if the update cannot be applied.
102func (txn *Transaction) ApplyUpdate(update *Update) error {
103 if txn.ptr == nil || !txn.IsWriteable() {
104 return fmt.Errorf("transaction is not writeable")
105 }
106 if update == nil || len(update.data) == 0 {
107 return nil
108 }
109
110 result := C.ytransaction_apply(txn.ptr,
111 (*C.char)(unsafe.Pointer(&update.data[0])),
112 C.uint32_t(len(update.data)))
113
114 if result != 0 {
115 return fmt.Errorf("failed to apply update, error code: %d", result)
116 }
117 return nil
118}
119
120// GetStateDiffV2 returns a delta update using v2 encoding.
121func (txn *Transaction) GetStateDiffV2(remoteSV *StateVector) *Update {
122 if txn.ptr == nil {
123 return nil
124 }
125
126 var length C.uint32_t
127 var ptr *C.char
128
129 if remoteSV == nil || len(remoteSV.data) == 0 {
130 ptr = C.ytransaction_state_diff_v2(txn.ptr, nil, 0, &length)
131 } else {
132 ptr = C.ytransaction_state_diff_v2(txn.ptr,
133 (*C.char)(unsafe.Pointer(&remoteSV.data[0])),
134 C.uint32_t(len(remoteSV.data)), &length)
135 }
136
137 if ptr == nil {
138 return nil
139 }
140 defer C.ybinary_destroy(ptr, length)
141
142 data := make([]byte, int(length))
143 copy(data, (*[1 << 30]byte)(unsafe.Pointer(ptr))[:length:length])
144 return &Update{data: data}
145}
146
147// ApplyUpdateV2 applies a v2 encoded update.
148func (txn *Transaction) ApplyUpdateV2(update *Update) error {
149 if txn.ptr == nil || !txn.IsWriteable() {
150 return fmt.Errorf("transaction is not writeable")
151 }
152 if update == nil || len(update.data) == 0 {
153 return nil
154 }
155
156 result := C.ytransaction_apply_v2(txn.ptr,
157 (*C.char)(unsafe.Pointer(&update.data[0])),
158 C.uint32_t(len(update.data)))
159
160 if result != 0 {
161 return fmt.Errorf("failed to apply v2 update, error code: %d", result)
162 }
163 return nil
164}