1use alloc::format;
2use alloc::string::{String, ToString};
3use alloc::vec::Vec;
4use core::mem;
5
6use crate::common::MAX_BLOCK_SIZE;
7#[cfg(feature = "hash")]
8use core::hash::Hasher;
9#[cfg(feature = "hash")]
10use twox_hash::XxHash64;
11
12use crate::encoding::levels::compress_block_encoded;
13use crate::encoding::{
14 CompressionLevel, EncoderDictionary, MatchGeneratorDriver, Matcher, block_header::BlockHeader,
15 frame_compressor::CachedDictionaryEntropy, frame_compressor::CompressState,
16 frame_compressor::FseTables, frame_compressor::PreviousFseTable, frame_header::FrameHeader,
17};
18use crate::io::{Error, ErrorKind, Write};
19
20pub struct StreamingEncoder<W: Write, M: Matcher = MatchGeneratorDriver> {
26 drain: Option<W>,
27 compression_level: CompressionLevel,
28 state: CompressState<M>,
29 pending: Vec<u8>,
30 encoded_scratch: Vec<u8>,
31 errored: bool,
32 last_error_kind: Option<ErrorKind>,
33 last_error_message: Option<String>,
34 frame_started: bool,
35 target_block_size: Option<u32>,
39 pledged_content_size: Option<u64>,
40 source_size_hint: Option<u64>,
46 content_size_flag: bool,
52 bytes_consumed: u64,
53 savings: i64,
57 strategy_override: Option<(crate::encoding::strategy::StrategyTag, u8)>,
64 target_length_override: Option<u32>,
68 magicless: bool,
71 content_checksum: bool,
77 dictionary: Option<EncoderDictionary>,
80 dictionary_id_flag: bool,
86 dictionary_entropy_cache: Option<CachedDictionaryEntropy>,
90 #[cfg(feature = "hash")]
91 hasher: XxHash64,
92}
93
94impl<W: Write> StreamingEncoder<W, MatchGeneratorDriver> {
95 pub fn new(drain: W, compression_level: CompressionLevel) -> Self {
100 Self::new_with_matcher(
101 MatchGeneratorDriver::new(MAX_BLOCK_SIZE as usize, 1),
102 drain,
103 compression_level,
104 )
105 }
106
107 pub fn set_parameters(
115 &mut self,
116 params: &crate::encoding::CompressionParameters,
117 ) -> Result<(), Error> {
118 self.ensure_open()?;
119 if self.frame_started {
120 return Err(invalid_input_error(
121 "compression parameters must be set before the first write",
122 ));
123 }
124 self.compression_level = params.level();
125 let overrides = params.overrides();
126 self.strategy_override = overrides.strategy.map(|s| (s.tag(), s.lazy_depth()));
129 self.target_length_override = overrides.target_length;
130 self.state.strategy_tag = self.strategy_override.map_or_else(
131 || {
132 crate::encoding::strategy::StrategyTag::for_compression_level(
133 self.compression_level,
134 )
135 },
136 |(tag, _)| tag,
137 );
138 self.state.huf_optimal_search = crate::encoding::frame_compressor::huf_search_enabled(
139 self.state.strategy_tag,
140 self.pledged_content_size.or(self.source_size_hint),
141 );
142 self.state.matcher.set_param_overrides(Some(overrides));
143 Ok(())
144 }
145}
146
147impl<W: Write, M: Matcher> StreamingEncoder<W, M> {
148 pub fn new_with_matcher(matcher: M, drain: W, compression_level: CompressionLevel) -> Self {
153 Self {
154 drain: Some(drain),
155 compression_level,
156 state: CompressState {
157 matcher,
158 copy_tier: crate::decoding::simd_copy::ExactCopyTier::resolve(),
159 last_huff_table: None,
160 huff_table_spare: None,
161 huff_rollback: None,
162 huff_weights: Default::default(),
163 seen_content: Default::default(),
164 fse_tables: FseTables::new(),
165 block_scratch: crate::encoding::blocks::CompressedBlockScratch::new(),
166 offset_hist: [1, 4, 8],
167 strategy_tag: crate::encoding::strategy::StrategyTag::for_compression_level(
168 compression_level,
169 ),
170 pre_split: crate::encoding::levels::config::level_pre_split(compression_level)
171 .map(|tier| tier as u8),
172 huf_optimal_search: true,
173 literal_compression_disabled: matches!(
174 compression_level,
175 CompressionLevel::Level(n) if n < 0
176 ),
177 },
178 pending: Vec::new(),
179 encoded_scratch: Vec::new(),
180 errored: false,
181 last_error_kind: None,
182 last_error_message: None,
183 frame_started: false,
184 target_block_size: None,
185 pledged_content_size: None,
186 source_size_hint: None,
187 content_size_flag: true,
188 bytes_consumed: 0,
189 savings: 0,
190 strategy_override: None,
191 target_length_override: None,
192 magicless: false,
193 content_checksum: false,
194 dictionary: None,
195 dictionary_id_flag: true,
196 dictionary_entropy_cache: None,
197 #[cfg(feature = "hash")]
198 hasher: XxHash64::with_seed(0),
199 }
200 }
201
202 pub fn set_target_block_size(&mut self, target: Option<u32>) -> Result<(), Error> {
211 self.ensure_open()?;
212 if self.frame_started {
213 return Err(invalid_input_error(
214 "the block-size target must be set before the first write",
215 ));
216 }
217 self.target_block_size = target.map(|t| {
218 t.clamp(
219 crate::common::MIN_TARGET_BLOCK_SIZE,
220 crate::common::MAX_BLOCK_SIZE,
221 )
222 });
223 Ok(())
224 }
225
226 pub fn set_content_checksum(&mut self, emit: bool) -> Result<(), Error> {
234 self.ensure_open()?;
235 if self.frame_started {
236 return Err(invalid_input_error(
237 "content checksum must be set before the first write",
238 ));
239 }
240 self.content_checksum = emit;
241 Ok(())
242 }
243
244 pub fn set_magicless(&mut self, magicless: bool) -> Result<(), Error> {
252 self.ensure_open()?;
253 if self.frame_started {
254 return Err(invalid_input_error(
255 "magicless format must be set before the first write",
256 ));
257 }
258 self.magicless = magicless;
259 Ok(())
260 }
261
262 pub fn set_pledged_content_size(&mut self, size: u64) -> Result<(), Error> {
273 self.ensure_open()?;
274 if self.frame_started {
275 return Err(invalid_input_error(
276 "pledged content size must be set before the first write",
277 ));
278 }
279 self.pledged_content_size = Some(size);
280 self.state.matcher.set_source_size_hint(size);
283 Ok(())
284 }
285
286 pub fn set_content_size_flag(&mut self, emit: bool) -> Result<(), Error> {
293 self.ensure_open()?;
294 if self.frame_started {
295 return Err(invalid_input_error(
296 "content size flag must be set before the first write",
297 ));
298 }
299 self.content_size_flag = emit;
300 Ok(())
301 }
302
303 pub fn set_source_size_hint(&mut self, size: u64) -> Result<(), Error> {
311 self.ensure_open()?;
312 if self.frame_started {
313 return Err(invalid_input_error(
314 "source size hint must be set before the first write",
315 ));
316 }
317 self.state.matcher.set_source_size_hint(size);
318 self.source_size_hint = Some(size);
323 Ok(())
324 }
325
326 pub fn set_dictionary_from_bytes(&mut self, raw_dictionary: &[u8]) -> Result<(), Error> {
337 if raw_dictionary.is_empty() {
338 self.ensure_open()?;
342 if self.frame_started {
343 return Err(invalid_input_error(
344 "dictionary must be attached before the first write",
345 ));
346 }
347 self.dictionary_entropy_cache = None;
355 self.dictionary = None;
356 return Ok(());
357 }
358 let dict = EncoderDictionary::from_serialized_or_raw_content(raw_dictionary)
359 .map_err(|err| invalid_input_error(&alloc::format!("invalid dictionary: {err:?}")))?;
360 self.set_encoder_dictionary(dict)
361 }
362
363 pub fn set_dictionary_id_flag(&mut self, emit: bool) -> Result<(), Error> {
368 self.ensure_open()?;
369 if self.frame_started {
370 return Err(invalid_input_error(
371 "dictionary ID flag must be set before the first write",
372 ));
373 }
374 self.dictionary_id_flag = emit;
375 Ok(())
376 }
377
378 pub fn set_encoder_dictionary(&mut self, dict: EncoderDictionary) -> Result<(), Error> {
382 self.ensure_open()?;
383 if self.frame_started {
384 return Err(invalid_input_error(
385 "dictionary must be attached before the first write",
386 ));
387 }
388 let inner = &dict.inner;
392 if inner.offset_hist.contains(&0) {
393 return Err(invalid_input_error(
394 "dictionary carries a zero repeat offset",
395 ));
396 }
397 self.dictionary_entropy_cache = Some(CachedDictionaryEntropy::from_dictionary(inner));
398 self.dictionary = Some(dict);
399 Ok(())
400 }
401
402 pub fn get_ref(&self) -> &W {
407 self.drain
408 .as_ref()
409 .expect("streaming encoder drain is present until finish consumes self")
410 }
411
412 pub fn heap_size(&self) -> usize {
421 let mut total = self.state.matcher.heap_size();
422 total += self
423 .state
424 .last_huff_table
425 .as_ref()
426 .map_or(0, |table| table.heap_size());
427 total += self
428 .state
429 .huff_table_spare
430 .as_ref()
431 .map_or(0, |table| table.heap_size());
432 total += self.state.huff_weights.heap_size();
434 total += self.state.retained_scratch_heap_size();
435 total += self.state.seen_content.heap_size();
436 total += self.pending.capacity();
437 total += self.encoded_scratch.capacity();
438 total += self
439 .dictionary
440 .as_ref()
441 .map_or(0, |d| d.inner.dict_content.capacity());
442 total += self
443 .dictionary_entropy_cache
444 .as_ref()
445 .map_or(0, CachedDictionaryEntropy::heap_size);
446 total
447 }
448
449 pub fn get_mut(&mut self) -> &mut W {
457 self.drain
458 .as_mut()
459 .expect("streaming encoder drain is present until finish consumes self")
460 }
461
462 pub fn finish(mut self) -> Result<W, Error> {
467 self.ensure_open()?;
468
469 if let Some(pledged) = self.pledged_content_size
473 && self.bytes_consumed != pledged
474 {
475 return Err(invalid_input_error(
476 "pledged content size does not match bytes consumed",
477 ));
478 }
479
480 self.ensure_frame_started()?;
481
482 if self.pending.is_empty() {
483 self.write_empty_last_block()
484 .map_err(|err| self.fail(err))?;
485 } else {
486 self.emit_pending_block(true)?;
487 }
488
489 let mut drain = self
490 .drain
491 .take()
492 .expect("streaming encoder drain must be present when finishing");
493
494 #[cfg(feature = "hash")]
495 if self.content_checksum {
496 let checksum = self.hasher.finish() as u32;
497 drain
498 .write_all(&checksum.to_le_bytes())
499 .map_err(|err| self.fail(err))?;
500 }
501
502 drain.flush().map_err(|err| self.fail(err))?;
503 Ok(drain)
504 }
505
506 fn ensure_open(&self) -> Result<(), Error> {
507 if self.errored {
508 return Err(self.sticky_error());
509 }
510 Ok(())
511 }
512
513 fn sticky_error(&self) -> Error {
517 match (self.last_error_kind, self.last_error_message.as_deref()) {
518 (Some(kind), Some(message)) => error_with_kind_message(
519 kind,
520 format!(
521 "streaming encoder is in an errored state due to previous {kind:?} failure: {message}"
522 ),
523 ),
524 (Some(kind), None) => error_from_kind(kind),
525 (None, Some(message)) => other_error_owned(format!(
526 "streaming encoder is in an errored state: {message}"
527 )),
528 (None, None) => other_error("streaming encoder is in an errored state"),
529 }
530 }
531
532 fn drain_mut(&mut self) -> Result<&mut W, Error> {
533 self.drain
534 .as_mut()
535 .ok_or_else(|| other_error("streaming encoder has no active drain"))
536 }
537
538 fn ensure_frame_started(&mut self) -> Result<(), Error> {
539 if self.frame_started {
540 return Ok(());
541 }
542
543 self.state.seen_content.reset_for_frame();
546 self.state.fse_tables.park_previous_before_frame();
550 self.ensure_level_supported()?;
551 let use_dictionary_state =
559 !matches!(self.compression_level, CompressionLevel::Uncompressed)
560 && self.state.matcher.supports_dictionary_priming()
561 && self.dictionary.is_some();
562 if use_dictionary_state && let Some(dict) = self.dictionary.as_ref() {
565 self.state.matcher.set_dictionary_size_hint(dict.sizes());
566 }
567 if let Some(size) = self.pledged_content_size.or(self.source_size_hint) {
573 self.state.matcher.set_source_size_hint(size);
574 }
575 self.state.matcher.reset(self.compression_level);
576 self.state.offset_hist = if use_dictionary_state {
579 self.dictionary
580 .as_ref()
581 .map(|dict| dict.inner.offset_hist)
582 .unwrap_or([1, 4, 8])
583 } else {
584 [1, 4, 8]
585 };
586 if use_dictionary_state && let Some(dict) = self.dictionary.as_ref() {
591 let offset_hist = dict.inner.offset_hist;
592 self.state
593 .matcher
594 .prime_with_dictionary(dict.inner.dict_content.as_slice(), offset_hist);
595 }
596 if use_dictionary_state && let Some(cache) = self.dictionary_entropy_cache.as_ref() {
599 self.state.last_huff_table.clone_from(&cache.huff);
600 self.state
601 .fse_tables
602 .ll_previous
603 .clone_from(&cache.ll_previous);
604 self.state
605 .fse_tables
606 .ml_previous
607 .clone_from(&cache.ml_previous);
608 self.state
609 .fse_tables
610 .of_previous
611 .clone_from(&cache.of_previous);
612 let ll_entropy = match cache.ll_previous.as_ref() {
613 Some(PreviousFseTable::Custom(table)) => Some(table.as_ref()),
614 _ => None,
615 };
616 let ml_entropy = match cache.ml_previous.as_ref() {
617 Some(PreviousFseTable::Custom(table)) => Some(table.as_ref()),
618 _ => None,
619 };
620 let of_entropy = match cache.of_previous.as_ref() {
621 Some(PreviousFseTable::Custom(table)) => Some(table.as_ref()),
622 _ => None,
623 };
624 self.state.matcher.seed_dictionary_entropy(
625 self.state.last_huff_table.as_ref(),
626 ll_entropy,
627 ml_entropy,
628 of_entropy,
629 );
630 } else {
631 self.state.last_huff_table = None;
632 self.state.fse_tables.ll_previous = None;
633 self.state.fse_tables.ml_previous = None;
634 self.state.fse_tables.of_previous = None;
635 }
636 let hint = self.pledged_content_size.or(self.source_size_hint);
643 let (params, dict_frame) = crate::encoding::frame_compressor::resolve_frame_params(
644 self.compression_level,
645 hint,
646 self.dictionary.as_ref().filter(|_| use_dictionary_state),
647 );
648 crate::encoding::frame_compressor::sync_effective_strategy(
649 &mut self.state,
650 self.compression_level,
651 ¶ms,
652 self.strategy_override.filter(|_| !dict_frame),
653 );
654 self.state.huf_optimal_search =
655 crate::encoding::frame_compressor::huf_search_enabled(self.state.strategy_tag, hint);
656 self.state.literal_compression_disabled =
657 crate::encoding::frame_compressor::literal_compression_disabled(
658 self.state.strategy_tag,
659 self.compression_level,
660 self.target_length_override.filter(|_| !dict_frame),
661 );
662 self.savings = 0;
663 #[cfg(feature = "hash")]
664 {
665 self.hasher = XxHash64::with_seed(0);
666 }
667
668 let window_size = self.state.matcher.window_size();
669 if window_size == 0 {
670 return Err(invalid_input_error(
671 "matcher reported window_size == 0, which is invalid",
672 ));
673 }
674
675 let single_segment = self.content_size_flag
684 && !use_dictionary_state
685 && self
686 .pledged_content_size
687 .map(|size| (512..=(1 << 14)).contains(&size) && size <= window_size)
688 .unwrap_or(false);
689
690 let header = FrameHeader {
691 frame_content_size: if self.content_size_flag {
692 self.pledged_content_size
693 } else {
694 None
695 },
696 single_segment,
697 content_checksum: cfg!(feature = "hash") && self.content_checksum,
698 dictionary_id: if use_dictionary_state && self.dictionary_id_flag {
699 self.dictionary
702 .as_ref()
703 .map(|dict| dict.inner.id)
704 .filter(|id| *id != 0)
705 .map(u64::from)
706 } else {
707 None
708 },
709 window_size: if single_segment {
710 None
711 } else {
712 Some(window_size)
713 },
714 magicless: self.magicless,
715 };
716 let mut encoded_header = Vec::new();
717 header.serialize(&mut encoded_header);
718 self.drain_mut()
719 .and_then(|drain| drain.write_all(&encoded_header))
720 .map_err(|err| self.fail(err))?;
721
722 self.frame_started = true;
723 Ok(())
724 }
725
726 fn block_capacity(&self) -> usize {
727 let matcher_window = self.state.matcher.window_size() as usize;
728 let ceiling = self
729 .target_block_size
730 .map_or(MAX_BLOCK_SIZE as usize, |t| t as usize);
731 core::cmp::max(1, core::cmp::min(matcher_window, ceiling))
732 }
733
734 fn allocate_pending_space(&mut self, block_capacity: usize) -> Vec<u8> {
735 let mut space = match self.compression_level {
736 CompressionLevel::Fastest
737 | CompressionLevel::Default
738 | CompressionLevel::Better
739 | CompressionLevel::Best
740 | CompressionLevel::Level(_) => self.state.matcher.get_next_space(),
741 CompressionLevel::Uncompressed => Vec::new(),
742 };
743 space.clear();
744 if space.capacity() > block_capacity {
745 space.shrink_to(block_capacity);
746 }
747 if space.capacity() < block_capacity {
748 space.reserve(block_capacity - space.capacity());
749 }
750 space
751 }
752
753 fn pre_split_len(&self, block_capacity: usize, remaining: usize) -> usize {
760 if matches!(self.compression_level, CompressionLevel::Uncompressed) {
761 return self.pending.len();
762 }
763 crate::encoding::frame_compressor::optimal_block_size_with(
764 self.state.pre_split.map(usize::from),
765 &self.pending,
766 remaining,
767 block_capacity,
768 self.savings,
769 )
770 .min(self.pending.len())
771 }
772
773 fn emit_pending_prefix(
778 &mut self,
779 block_len: usize,
780 block_capacity: usize,
781 ) -> Result<(), Error> {
782 let mut suffix = self.allocate_pending_space(block_capacity);
783 suffix.extend_from_slice(&self.pending[block_len..]);
784 let mut block = mem::replace(&mut self.pending, suffix);
785 block.truncate(block_len);
786 if let Err((err, mut restored_block)) = self.encode_block(block, false) {
787 restored_block.extend_from_slice(&self.pending);
788 self.pending = restored_block;
789 return Err(err);
790 }
791 Ok(())
792 }
793
794 fn emit_full_pending_block(
795 &mut self,
796 block_capacity: usize,
797 consumed: usize,
798 ) -> Option<Result<usize, Error>> {
799 if self.pending.len() != block_capacity {
800 return None;
801 }
802 let block_len = self.pre_split_len(block_capacity, block_capacity);
803 if let Err(err) = self.emit_pending_prefix(block_len, block_capacity) {
804 let err = self.fail(err);
805 if consumed > 0 {
806 return Some(Ok(consumed));
807 }
808 return Some(Err(err));
809 }
810 None
811 }
812
813 fn emit_pending_block(&mut self, last_block: bool) -> Result<(), Error> {
814 let block_capacity = self.block_capacity();
815 if last_block {
816 while self.pending.len() == block_capacity {
820 let block_len = self.pre_split_len(block_capacity, self.pending.len());
821 if block_len == self.pending.len() {
822 break;
823 }
824 self.emit_pending_prefix(block_len, block_capacity)
825 .map_err(|err| self.fail(err))?;
826 }
827 }
828 let block = mem::take(&mut self.pending);
829 if let Err((err, restored_block)) = self.encode_block(block, last_block) {
830 self.pending = restored_block;
831 return Err(self.fail(err));
832 }
833 if !last_block {
834 self.pending = self.allocate_pending_space(block_capacity);
835 }
836 Ok(())
837 }
838
839 fn ensure_level_supported(&self) -> Result<(), Error> {
843 match self.compression_level {
844 CompressionLevel::Uncompressed
845 | CompressionLevel::Fastest
846 | CompressionLevel::Default
847 | CompressionLevel::Better
848 | CompressionLevel::Best
849 | CompressionLevel::Level(_) => Ok(()),
850 }
851 }
852
853 fn encode_block(
854 &mut self,
855 uncompressed_data: Vec<u8>,
856 last_block: bool,
857 ) -> Result<(), (Error, Vec<u8>)> {
858 let mut raw_block = Some(uncompressed_data);
859 let mut encoded = Vec::new();
860 mem::swap(&mut encoded, &mut self.encoded_scratch);
861 encoded.clear();
862 let needed_capacity = self.block_capacity() + 3;
863 if encoded.capacity() < needed_capacity {
864 encoded.reserve(needed_capacity.saturating_sub(encoded.len()));
865 }
866 let mut moved_into_matcher = false;
867 let raw_len = raw_block.as_ref().map_or(0, Vec::len);
868 if raw_block.as_ref().is_some_and(|block| block.is_empty()) {
869 let header = BlockHeader {
870 last_block,
871 block_type: crate::blocks::block::BlockType::Raw,
872 block_size: 0,
873 };
874 header.serialize(&mut encoded);
875 } else {
876 match self.compression_level {
877 CompressionLevel::Uncompressed => {
878 let block = raw_block.as_ref().expect("raw block missing");
879 let header = BlockHeader {
880 last_block,
881 block_type: crate::blocks::block::BlockType::Raw,
882 block_size: block.len() as u32,
883 };
884 header.serialize(&mut encoded);
885 encoded.extend_from_slice(block);
886 }
887 CompressionLevel::Fastest
888 | CompressionLevel::Default
889 | CompressionLevel::Better
890 | CompressionLevel::Best
891 | CompressionLevel::Level(_) => {
892 let block = raw_block.take().expect("raw block missing");
893 debug_assert!(!block.is_empty(), "empty blocks handled above");
894 let dict_active = self.dictionary.is_some()
895 && self.state.matcher.supports_dictionary_priming();
896 compress_block_encoded(
897 &mut self.state,
898 self.compression_level,
899 last_block,
900 crate::encoding::levels::BlockInput::Staged(block),
901 &mut encoded,
902 dict_active,
903 #[cfg(feature = "lsm")]
906 None,
907 #[cfg(all(feature = "lsm", feature = "hash"))]
908 None,
909 );
910 moved_into_matcher = true;
911 }
912 }
913 }
914
915 if let Err(err) = self.drain_mut().and_then(|drain| drain.write_all(&encoded)) {
916 encoded.clear();
917 mem::swap(&mut encoded, &mut self.encoded_scratch);
918 let restored = if moved_into_matcher {
919 self.state.matcher.get_last_space().to_vec()
920 } else {
921 raw_block.unwrap_or_default()
922 };
923 return Err((err, restored));
924 }
925 self.savings += raw_len as i64 - encoded.len() as i64;
928
929 if moved_into_matcher {
930 #[cfg(feature = "hash")]
931 if self.content_checksum {
932 self.hasher.write(self.state.matcher.get_last_space());
933 }
934 } else {
935 self.hash_block(raw_block.as_deref().unwrap_or(&[]));
936 }
937 encoded.clear();
938 mem::swap(&mut encoded, &mut self.encoded_scratch);
939 Ok(())
940 }
941
942 fn write_empty_last_block(&mut self) -> Result<(), Error> {
943 self.encode_block(Vec::new(), true).map_err(|(err, _)| err)
944 }
945
946 fn fail(&mut self, err: Error) -> Error {
947 self.errored = true;
948 if self.last_error_kind.is_none() {
949 self.last_error_kind = Some(err.kind());
950 }
951 if self.last_error_message.is_none() {
952 self.last_error_message = Some(err.to_string());
953 }
954 err
955 }
956
957 #[cfg(feature = "hash")]
958 fn hash_block(&mut self, uncompressed_data: &[u8]) {
959 if self.content_checksum {
960 self.hasher.write(uncompressed_data);
961 }
962 }
963
964 #[cfg(not(feature = "hash"))]
965 fn hash_block(&mut self, _uncompressed_data: &[u8]) {}
966}
967
968impl<W: Write, M: Matcher> Write for StreamingEncoder<W, M> {
969 fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
970 self.ensure_open()?;
971 if buf.is_empty() {
972 return Ok(0);
973 }
974
975 if let Some(pledged) = self.pledged_content_size
979 && self.bytes_consumed >= pledged
980 {
981 return Err(invalid_input_error(
982 "write would exceed pledged content size",
983 ));
984 }
985
986 self.ensure_frame_started()?;
987
988 let buf = if let Some(pledged) = self.pledged_content_size {
992 let remaining_allowed = pledged
993 .checked_sub(self.bytes_consumed)
994 .ok_or_else(|| invalid_input_error("bytes consumed exceed pledged content size"))?;
995 if remaining_allowed == 0 {
996 return Err(invalid_input_error(
997 "write would exceed pledged content size",
998 ));
999 }
1000 let accepted = core::cmp::min(
1001 buf.len(),
1002 usize::try_from(remaining_allowed).unwrap_or(usize::MAX),
1003 );
1004 &buf[..accepted]
1005 } else {
1006 buf
1007 };
1008
1009 let block_capacity = self.block_capacity();
1010 if self.pending.capacity() == 0 {
1011 self.pending = self.allocate_pending_space(block_capacity);
1012 }
1013 let mut remaining = buf;
1014 let mut consumed = 0usize;
1015
1016 while !remaining.is_empty() {
1017 if let Some(result) = self.emit_full_pending_block(block_capacity, consumed) {
1018 return result;
1019 }
1020
1021 let available = block_capacity - self.pending.len();
1022 let to_take = core::cmp::min(remaining.len(), available);
1023 if to_take == 0 {
1024 break;
1025 }
1026 self.pending.extend_from_slice(&remaining[..to_take]);
1027 remaining = &remaining[to_take..];
1028 consumed += to_take;
1029
1030 if let Some(result) = self.emit_full_pending_block(block_capacity, consumed) {
1031 if let Ok(n) = &result {
1032 self.bytes_consumed += *n as u64;
1033 }
1034 return result;
1035 }
1036 }
1037 self.bytes_consumed += consumed as u64;
1038 Ok(consumed)
1039 }
1040
1041 fn flush(&mut self) -> Result<(), Error> {
1042 self.ensure_open()?;
1043 if self.pending.is_empty() {
1044 return self
1045 .drain_mut()
1046 .and_then(|drain| drain.flush())
1047 .map_err(|err| self.fail(err));
1048 }
1049 self.ensure_frame_started()?;
1050 self.emit_pending_block(false)?;
1051 self.drain_mut()
1052 .and_then(|drain| drain.flush())
1053 .map_err(|err| self.fail(err))
1054 }
1055}
1056
1057fn error_from_kind(kind: ErrorKind) -> Error {
1058 Error::from(kind)
1059}
1060
1061fn error_with_kind_message(kind: ErrorKind, message: String) -> Error {
1062 #[cfg(feature = "std")]
1063 {
1064 Error::new(kind, message)
1065 }
1066 #[cfg(not(feature = "std"))]
1067 {
1068 Error::new(kind, alloc::boxed::Box::new(message))
1069 }
1070}
1071
1072fn invalid_input_error(message: &str) -> Error {
1073 #[cfg(feature = "std")]
1074 {
1075 Error::new(ErrorKind::InvalidInput, message)
1076 }
1077 #[cfg(not(feature = "std"))]
1078 {
1079 Error::new(
1080 ErrorKind::Other,
1081 alloc::boxed::Box::new(alloc::string::String::from(message)),
1082 )
1083 }
1084}
1085
1086fn other_error_owned(message: String) -> Error {
1087 #[cfg(feature = "std")]
1088 {
1089 Error::other(message)
1090 }
1091 #[cfg(not(feature = "std"))]
1092 {
1093 Error::new(ErrorKind::Other, alloc::boxed::Box::new(message))
1094 }
1095}
1096
1097fn other_error(message: &str) -> Error {
1098 #[cfg(feature = "std")]
1099 {
1100 Error::other(message)
1101 }
1102 #[cfg(not(feature = "std"))]
1103 {
1104 Error::new(
1105 ErrorKind::Other,
1106 alloc::boxed::Box::new(alloc::string::String::from(message)),
1107 )
1108 }
1109}
1110
1111#[cfg(test)]
1112mod tests;