Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lib.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use serde::Serialize;
use std::{
    io::{self, Write},
    num::NonZeroUsize,
};

/// A quantity of items that can fit into a payload, accounting for
/// serialization, encryption, and Base64-encoding overhead.
pub enum Fit {
    /// All items can fit into the payload.
    All,

    /// Some, but not all, items can fit into the payload without
    /// exceeding the maximum payload size.
    Some(NonZeroUsize),

    /// The maximum payload size is too small to hold any items.
    None,

    /// The serialized size of the items couldn't be determined because of
    /// a serialization error.
    Err(serde_json::Error),
}

impl Fit {
    /// If `self` is [`Fit::Some`], returns the number of items that can fit
    /// into the payload without exceeding its maximum size. Otherwise,
    /// returns `None`.
    #[inline]
    pub fn as_some(&self) -> Option<NonZeroUsize> {
        match self {
            Fit::Some(count) => Some(*count),
            _ => None,
        }
    }
}

/// A writer that counts the number of bytes it's asked to write, and discards
/// the data. Used to compute the serialized size of an item.
#[derive(Clone, Copy, Default)]
struct ByteCountWriter(usize);

impl ByteCountWriter {
    #[inline]
    pub fn count(self) -> usize {
        self.0
    }
}

impl Write for ByteCountWriter {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0 += buf.len();
        Ok(buf.len())
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

/// Returns the size of the given value, in bytes, when serialized to JSON.
pub fn compute_serialized_size<T: Serialize + ?Sized>(value: &T) -> serde_json::Result<usize> {
    let mut w = ByteCountWriter::default();
    serde_json::to_writer(&mut w, value)?;
    Ok(w.count())
}

/// Calculates the maximum number of items that can fit within
/// `max_payload_size` when serialized to JSON.
pub fn try_fit_items<T: Serialize>(items: &[T], max_payload_size: usize) -> Fit {
    let size = match compute_serialized_size(&items) {
        Ok(size) => size,
        Err(e) => return Fit::Err(e),
    };
    // See bug 535326 comment 8 for an explanation of the estimation
    let max_serialized_size = match ((max_payload_size / 4) * 3).checked_sub(1500) {
        Some(max_serialized_size) => max_serialized_size,
        None => return Fit::None,
    };
    if size > max_serialized_size {
        // Estimate a little more than the direct fraction to maximize packing
        let mut cutoff = (items.len() * max_serialized_size - 1) / size + 1;
        // Keep dropping off the last entry until the data fits.
        while cutoff > 0 {
            let size = match compute_serialized_size(&items[..cutoff]) {
                Ok(size) => size,
                Err(e) => return Fit::Err(e),
            };
            if size <= max_serialized_size {
                break;
            }
            cutoff -= 1;
        }
        match NonZeroUsize::new(cutoff) {
            Some(count) => Fit::Some(count),
            None => Fit::None,
        }
    } else {
        Fit::All
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use serde_derive::Serialize;

    #[derive(Serialize)]
    struct CommandRecord {
        #[serde(rename = "command")]
        name: &'static str,
        #[serde(default)]
        args: &'static [Option<&'static str>],
        #[serde(default, rename = "flowID", skip_serializing_if = "Option::is_none")]
        flow_id: Option<&'static str>,
    }

    const COMMANDS: &[CommandRecord] = &[
        CommandRecord {
            name: "wipeEngine",
            args: &[Some("bookmarks")],
            flow_id: Some("flow"),
        },
        CommandRecord {
            name: "resetEngine",
            args: &[Some("history")],
            flow_id: Some("flow"),
        },
        CommandRecord {
            name: "logout",
            args: &[],
            flow_id: None,
        },
    ];

    #[test]
    fn test_compute_serialized_size() {
        assert_eq!(compute_serialized_size(&1).unwrap(), 1);
        assert_eq!(compute_serialized_size(&"hi").unwrap(), 4);
        assert_eq!(
            compute_serialized_size(&["hi", "hello", "bye"]).unwrap(),
            20
        );

        let sizes = COMMANDS
            .iter()
            .map(|c| compute_serialized_size(c).unwrap())
            .collect::<Vec<_>>();
        assert_eq!(sizes, &[61, 60, 30]);
    }

    #[test]
    fn test_try_fit_items() {
        // 4096 bytes is enough to fit all three commands.
        assert!(matches!(try_fit_items(COMMANDS, 4096), Fit::All));

        // `logout` won't fit within 2168 bytes.
        assert_eq!(try_fit_items(COMMANDS, 2168).as_some().unwrap().get(), 2);

        // `resetEngine` won't fit within 2084 bytes.
        assert_eq!(try_fit_items(COMMANDS, 2084).as_some().unwrap().get(), 1);

        // `wipeEngine` won't fit at all.
        assert!(matches!(try_fit_items(COMMANDS, 1024), Fit::None));
    }
}

[ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge