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 error @ ValueRetentionError::Malformed { .. } => super::decoding::malformed_value(&error),
144 }
145}
146
147#[derive(Clone, Debug)]
149pub struct RetainedStoredDocument {
150 fields: RetainedDocumentFields,
151 metadata: DocumentMetadata,
152}
153
154impl RetainedStoredDocument {
155 pub fn with_metadata(fields: RetainedDocumentFields, metadata: DocumentMetadata) -> Self {
156 Self { fields, metadata }
157 }
158
159 pub fn fields(&self) -> &Document {
160 &self.fields
161 }
162
163 pub fn retained_fields(&self) -> &RetainedDocumentFields {
164 &self.fields
165 }
166
167 pub fn metadata(&self) -> DocumentMetadata {
168 self.metadata
169 }
170
171 pub fn into_parts(self) -> (RetainedDocumentFields, DocumentMetadata) {
172 (self.fields, self.metadata)
173 }
174
175 pub fn into_stored(self) -> StoredDocument {
177 StoredDocument::with_metadata(self.fields.into_document(), self.metadata)
178 }
179
180 pub fn into_budgeted(
182 self,
183 control: &StorageReadControl,
184 ) -> StorageBackendResult<Budgeted<StoredDocument>> {
185 let (fields, memory) = self.fields.into_budgeted(control)?.into_parts();
186 Ok(Budgeted::new(
187 StoredDocument::with_metadata(fields, self.metadata),
188 memory,
189 ))
190 }
191}
192
193impl AsRef<Document> for RetainedDocumentFields {
194 fn as_ref(&self) -> &Document {
195 &self.0
196 }
197}
198
199impl std::ops::Deref for RetainedDocumentFields {
200 type Target = Document;
201
202 fn deref(&self) -> &Self::Target {
203 self.as_ref()
204 }
205}
206
207#[cfg(test)]
208mod tests;