uqa_storage/document_store/
retained.rs1use super::{Arc, Document, DocumentMetadata, StoredDocument};
10use crate::{read_control::StorageReadControl, StorageBackendError, StorageBackendResult};
11use uqa_core::{
12 memory::{Budgeted, MemoryError},
13 FieldName, Value, ValueRetentionError,
14};
15
16#[derive(Clone, Debug)]
18pub struct RetainedDocumentFields(Arc<Budgeted<Arc<Document>>>);
19
20impl RetainedDocumentFields {
21 pub fn retain_with_control(&self, control: &StorageReadControl) -> StorageBackendResult<Self> {
23 control.check()?;
24 if self.shares_allowance(control) {
25 Ok(self.clone())
26 } else {
27 Self::new(Arc::clone(&**self.0), control)
28 }
29 }
30
31 pub(super) fn shares_allowance(&self, control: &StorageReadControl) -> bool {
32 self.0.budget().shares_allowance(control.memory())
33 }
34
35 pub fn from_budgeted(
37 fields: Budgeted<Document>,
38 control: &StorageReadControl,
39 ) -> StorageBackendResult<Self> {
40 control.check()?;
41 let (fields, mut memory) = fields.into_parts();
42 if !memory.budget().shares_allowance(control.memory()) {
43 drop(fields);
44 return Err(StorageBackendError::Other(
45 "decoded document reservation belongs to a different allowance".into(),
46 ));
47 }
48 let value = Value::Map(fields);
49 let required = value
50 .retained_payload_bytes(control.memory(), control.cancellation())
51 .map_err(retention_error)?;
52 if memory.bytes() < required {
53 return Err(StorageBackendError::Other(
54 "decoded document reservation does not cover its payload".into(),
55 ));
56 }
57 let surplus = memory.bytes() - required;
59 drop(memory.split(surplus));
60 memory.grow(size_of::<Document>())?;
61 let Value::Map(fields) = value else {
62 unreachable!("document field map");
63 };
64 let fields = Arc::new(fields);
65 control.check()?;
66 Ok(Self(Budgeted::new(fields, memory).into_shared()?))
67 }
68
69 pub fn new(fields: Arc<Document>, control: &StorageReadControl) -> StorageBackendResult<Self> {
70 control.check()?;
71 let entries = fields
72 .len()
73 .checked_mul(size_of::<(FieldName, Value)>())
74 .ok_or(MemoryError::SizeOverflow)?;
75 let mut memory = control.memory().reserve(size_of::<Document>())?;
76 memory.grow(entries)?;
77 for (name, value) in fields.iter() {
78 control.check()?;
79 memory.grow(name.capacity())?;
80 let payload = value
81 .reserve_retained_payload(control.memory(), control.cancellation())
82 .map_err(retention_error)?;
83 memory.absorb(payload);
84 }
85 control.check()?;
86 Ok(Self(Budgeted::new(fields, memory).into_shared()?))
87 }
88
89 pub fn into_document(self) -> Document {
91 match Arc::try_unwrap(self.0) {
92 Ok(fields) => {
93 let (fields, _memory) = fields.into_parts();
94 Arc::unwrap_or_clone(fields)
95 }
96 Err(fields) => (**fields).as_ref().clone(),
97 }
98 }
99
100 pub fn into_budgeted(
102 self,
103 control: &StorageReadControl,
104 ) -> StorageBackendResult<Budgeted<Document>> {
105 control.check()?;
106 if !self.shares_allowance(control) {
107 return Err(StorageBackendError::Other(
108 "retained document belongs to a different allowance".into(),
109 ));
110 }
111 let shared = match Arc::try_unwrap(self.0) {
112 Ok(fields) => {
113 let (fields, mut memory) = fields.into_parts();
114 drop(memory.split(size_of::<Budgeted<Arc<Document>>>()));
115 return match Arc::try_unwrap(fields) {
116 Ok(fields) => {
117 drop(memory.split(size_of::<Document>()));
118 control.check()?;
119 Ok(Budgeted::new(fields, memory))
120 }
121 Err(fields) => {
122 let original = Budgeted::new(fields, memory);
123 super::controlled_rows::copy_fields_budgeted(
124 original.iter().map(|(name, value)| (name.as_str(), value)),
125 control,
126 )
127 }
128 };
129 }
130 Err(shared) => shared,
131 };
132 super::controlled_rows::copy_fields_budgeted(
133 shared.iter().map(|(name, value)| (name.as_str(), value)),
134 control,
135 )
136 }
137}
138
139pub(super) fn retention_error(error: ValueRetentionError) -> StorageBackendError {
140 match error {
141 ValueRetentionError::Memory(error) => StorageBackendError::Memory(error),
142 ValueRetentionError::Cancelled(error) => StorageBackendError::Cancelled(error),
143 }
144}
145
146#[derive(Clone, Debug)]
148pub struct RetainedStoredDocument {
149 fields: RetainedDocumentFields,
150 metadata: DocumentMetadata,
151}
152
153impl RetainedStoredDocument {
154 pub fn with_metadata(fields: RetainedDocumentFields, metadata: DocumentMetadata) -> Self {
155 Self { fields, metadata }
156 }
157
158 pub fn fields(&self) -> &Document {
159 &self.fields
160 }
161
162 pub fn retained_fields(&self) -> &RetainedDocumentFields {
163 &self.fields
164 }
165
166 pub fn metadata(&self) -> DocumentMetadata {
167 self.metadata
168 }
169
170 pub fn into_parts(self) -> (RetainedDocumentFields, DocumentMetadata) {
171 (self.fields, self.metadata)
172 }
173
174 pub fn into_stored(self) -> StoredDocument {
176 StoredDocument::with_metadata(self.fields.into_document(), self.metadata)
177 }
178
179 pub fn into_budgeted(
181 self,
182 control: &StorageReadControl,
183 ) -> StorageBackendResult<Budgeted<StoredDocument>> {
184 let (fields, memory) = self.fields.into_budgeted(control)?.into_parts();
185 Ok(Budgeted::new(
186 StoredDocument::with_metadata(fields, self.metadata),
187 memory,
188 ))
189 }
190}
191
192impl AsRef<Document> for RetainedDocumentFields {
193 fn as_ref(&self) -> &Document {
194 &self.0
195 }
196}
197
198impl std::ops::Deref for RetainedDocumentFields {
199 type Target = Document;
200
201 fn deref(&self) -> &Self::Target {
202 self.as_ref()
203 }
204}
205
206#[cfg(test)]
207mod tests;