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}