1#[cfg(any(feature = "epee", feature = "serde"))]
5use monero_oxide::io::VarInt;
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9#[cfg(feature = "epee")]
10use cuprate_epee_encoding::{
11 container_as_blob::ContainerAsBlob,
12 epee_object, error,
13 macros::bytes::{Buf, BufMut},
14 read_epee_value, read_marker, write_field, EpeeObject, EpeeObjectBuilder, EpeeValue,
15};
16
17#[doc = crate::macros::monero_definition_link!(
20 "cc73fe71162d564ffda8e549b79a350bca53c454",
21 "rpc/core_rpc_server_commands_defs.h",
22 45..=55
23)]
24#[cfg(any(feature = "epee", feature = "serde"))]
25fn compress_integer_array(v: &[u64]) -> Vec<u8> {
26 let mut out = Vec::with_capacity(v.len() * 2);
27 for val in v {
28 VarInt::write(val, &mut out).expect("Writing to vec should not fail");
29 }
30 out
31}
32
33#[doc = crate::macros::monero_definition_link!(
35 "cc73fe71162d564ffda8e549b79a350bca53c454",
36 "rpc/core_rpc_server_commands_defs.h",
37 57..=72
38)]
39#[cfg(any(feature = "epee", feature = "serde"))]
40fn decompress_integer_array(mut s: &[u8]) -> std::io::Result<Vec<u64>> {
41 let mut v = Vec::new();
42 while !s.is_empty() {
43 v.push(VarInt::read(&mut s)?);
44 }
45 Ok(v)
46}
47
48#[doc = crate::macros::monero_definition_link!(
50 "cc73fe71162d564ffda8e549b79a350bca53c454",
51 "rpc/core_rpc_server_commands_defs.h",
52 2468..=2508
53)]
54#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
72#[cfg_attr(feature = "serde", serde(untagged))]
73pub enum Distribution {
74 Uncompressed(DistributionUncompressed),
76 CompressedBinary(DistributionCompressedBinary),
78}
79
80impl Default for Distribution {
81 fn default() -> Self {
82 Self::Uncompressed(DistributionUncompressed::default())
83 }
84}
85
86#[cfg_attr(feature = "serde", derive(Deserialize))]
88#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
89pub struct DistributionUncompressed {
90 pub start_height: u64,
91 pub base: u64,
92 pub distribution: Vec<u64>,
94 pub amount: u64,
95 pub binary: bool,
96}
97
98#[cfg(feature = "serde")]
101impl Serialize for DistributionUncompressed {
102 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103 where
104 S: serde::Serializer,
105 {
106 use serde::ser::SerializeStruct;
107 let mut s = serializer.serialize_struct("DistributionUncompressed", 6)?;
108 s.serialize_field("start_height", &self.start_height)?;
109 s.serialize_field("base", &self.base)?;
110 s.serialize_field("distribution", &self.distribution)?;
111 s.serialize_field("amount", &self.amount)?;
112 s.serialize_field("binary", &self.binary)?;
113 s.serialize_field("compress", &false)?;
114 s.end()
115 }
116}
117
118#[cfg(feature = "epee")]
124#[derive(Default)]
125pub struct __DistributionUncompressedEpeeBuilder;
126
127#[cfg(feature = "epee")]
128impl EpeeObjectBuilder<DistributionUncompressed> for __DistributionUncompressedEpeeBuilder {
129 fn add_field<B: Buf>(&mut self, _: &str, _: &mut B) -> error::Result<bool> {
130 unreachable!("DistributionUncompressed is never deserialized as a standalone EPEE object")
131 }
132
133 fn finish(self) -> error::Result<DistributionUncompressed> {
134 unreachable!("DistributionUncompressed is never deserialized as a standalone EPEE object")
135 }
136}
137
138#[cfg(feature = "epee")]
139impl EpeeObject for DistributionUncompressed {
140 type Builder = __DistributionUncompressedEpeeBuilder;
141
142 fn number_of_fields(&self) -> u64 {
143 4 + u64::from(EpeeValue::should_write(&self.distribution))
144 }
145
146 fn write_fields<B: BufMut>(self, _: &mut B) -> error::Result<()> {
147 unreachable!() }
149}
150
151#[cfg_attr(feature = "serde", derive(Deserialize))]
153#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
154pub struct DistributionCompressedBinary {
155 pub start_height: u64,
156 pub base: u64,
157 #[cfg_attr(
158 feature = "serde",
159 serde(deserialize_with = "deserialize_compressed_data_as_distribution")
160 )]
161 #[cfg_attr(feature = "serde", serde(rename = "compressed_data"))]
162 pub distribution: Vec<u64>,
163 pub amount: u64,
164}
165
166#[cfg(feature = "serde")]
169impl Serialize for DistributionCompressedBinary {
170 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171 where
172 S: serde::Serializer,
173 {
174 use serde::ser::SerializeStruct;
175 let mut s = serializer.serialize_struct("DistributionCompressedBinary", 6)?;
176 s.serialize_field("start_height", &self.start_height)?;
177 s.serialize_field("base", &self.base)?;
178 s.serialize_field(
179 "compressed_data",
180 &compress_integer_array(&self.distribution),
181 )?;
182 s.serialize_field("amount", &self.amount)?;
183 s.serialize_field("binary", &true)?;
184 s.serialize_field("compress", &true)?;
185 s.end()
186 }
187}
188
189#[cfg(feature = "epee")]
190epee_object! {
191 DistributionCompressedBinary,
192 start_height: u64,
193 base: u64,
194 distribution: Vec<u64>,
195 amount: u64,
196}
197
198#[cfg(feature = "serde")]
203fn deserialize_compressed_data_as_distribution<'de, D>(d: D) -> Result<Vec<u64>, D::Error>
204where
205 D: serde::Deserializer<'de>,
206{
207 Vec::<u8>::deserialize(d)
208 .and_then(|v| decompress_integer_array(&v).map_err(serde::de::Error::custom))
209}
210
211#[cfg(feature = "epee")]
213#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
217#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
218pub struct __DistributionEpeeBuilder {
219 pub start_height: Option<u64>,
220 pub base: Option<u64>,
221 pub distribution: Option<Vec<u64>>,
222 pub amount: Option<u64>,
223 pub compressed_data: Option<Vec<u8>>,
224 pub binary: Option<bool>,
225 pub compress: Option<bool>,
226}
227
228#[cfg(feature = "epee")]
229impl EpeeObjectBuilder<Distribution> for __DistributionEpeeBuilder {
230 fn add_field<B: Buf>(&mut self, name: &str, r: &mut B) -> error::Result<bool> {
231 match name {
232 "start_height" => self.start_height = Some(read_epee_value(r, Default::default())?),
233 "base" => self.base = Some(read_epee_value(r, Default::default())?),
234 "amount" => self.amount = Some(read_epee_value(r, Default::default())?),
235 "binary" => self.binary = Some(read_epee_value(r, Default::default())?),
236 "compress" => self.compress = Some(read_epee_value(r, Default::default())?),
237 "compressed_data" => {
238 self.compressed_data = Some(read_epee_value(r, Default::default())?);
239 }
240 "distribution" => {
244 let marker = read_marker(r)?;
245 self.distribution = Some(if marker == ContainerAsBlob::<u64>::MARKER {
246 ContainerAsBlob::<u64>::read(r, &marker, Default::default())?.into()
247 } else {
248 Vec::<u64>::read(r, &marker, Default::default())?
249 });
250 }
251 _ => return Ok(false),
252 }
253
254 Ok(true)
255 }
256
257 fn finish(self) -> error::Result<Distribution> {
258 const ELSE: error::Error = error::Error::Format("Required field was not found!");
259
260 let start_height = self.start_height.ok_or(ELSE)?;
261 let base = self.base.ok_or(ELSE)?;
262 let amount = self.amount.ok_or(ELSE)?;
263
264 let distribution = if let Some(compressed_data) = self.compressed_data {
265 let distribution = decompress_integer_array(&compressed_data)
266 .map_err(|_| error::Error::Format("Failed to decompress distribution"))?;
267 Distribution::CompressedBinary(DistributionCompressedBinary {
268 start_height,
269 base,
270 distribution,
271 amount,
272 })
273 } else {
274 let distribution = self.distribution.unwrap_or_default();
275 Distribution::Uncompressed(DistributionUncompressed {
276 binary: self.binary.unwrap_or(true),
277 distribution,
278 start_height,
279 base,
280 amount,
281 })
282 };
283
284 Ok(distribution)
285 }
286}
287
288#[cfg(feature = "epee")]
289impl EpeeObject for Distribution {
290 type Builder = __DistributionEpeeBuilder;
291
292 fn number_of_fields(&self) -> u64 {
293 match self {
294 Self::Uncompressed(s) => s.number_of_fields() + 1,
296 Self::CompressedBinary(s) => s.number_of_fields() + 2,
298 }
299 }
300
301 fn write_fields<B: BufMut>(self, w: &mut B) -> error::Result<()> {
302 match self {
303 Self::Uncompressed(DistributionUncompressed {
304 start_height,
305 base,
306 distribution,
307 amount,
308 binary,
309 }) => {
310 write_field(start_height, "start_height", w)?;
311 write_field(base, "base", w)?;
312 if binary {
313 write_field(ContainerAsBlob::from(distribution), "distribution", w)?;
314 } else {
315 write_field(distribution, "distribution", w)?;
316 }
317 write_field(amount, "amount", w)?;
318 write_field(binary, "binary", w)?;
319 write_field(false, "compress", w)?;
320 }
321
322 Self::CompressedBinary(DistributionCompressedBinary {
323 start_height,
324 base,
325 distribution,
326 amount,
327 }) => {
328 let compressed_data = compress_integer_array(&distribution);
329
330 write_field(start_height, "start_height", w)?;
331 write_field(base, "base", w)?;
332 write_field(compressed_data, "compressed_data", w)?;
333 write_field(amount, "amount", w)?;
334 write_field(true, "binary", w)?;
335 write_field(true, "compress", w)?;
336 }
337 }
338
339 Ok(())
340 }
341}