You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lolistealer/src/loli/encoding.rs

141 lines
3.4 KiB

use super::*;
use std::{
str,
};
const HEADER_BASE64_JPEG: &'static [u8] = b"/9j/";
const HEADER_BASE64_PNG: &'static [u8] = b"iVBO";
const HEADER_BASE64_GIF: &'static [u8] = b"R0lG";
const EXT_JPEG: &'static str = "jpg";
const EXT_PNG: &'static str = "png";
const EXT_GIF: &'static str = "gif";
/// An image type header
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum ImageType
{
Png,
Jpeg,
Gif,
}
impl Default for ImageType
{
fn default() -> Self
{
Self::Jpeg
}
}
impl ImageType
{
/// From base64 byte slice
///
/// # Notes
///
/// To parse from `str`, use the `FromStr` trait instead.
pub fn from_base64<T>(from: T) -> Result<Self, error::Error>
where T: AsRef<[u8]>
{
let from = from.as_ref();
if from.len() > 4 {
Ok(match &from[..4] {
HEADER_BASE64_GIF => Self::Gif,
HEADER_BASE64_PNG => Self::Png,
HEADER_BASE64_JPEG => Self::Jpeg,
_ => return Err(error::Error::UnknownFormat),
})
} else {
Err(error::Error::InvalidFormat)
}
}
/// Get the extension for this image type
pub fn ext(&self) -> &str
{
match self {
Self::Jpeg => EXT_JPEG,
Self::Png => EXT_PNG,
Self::Gif => EXT_GIF,
}
}
}
impl str::FromStr for ImageType
{
type Err = error::Error;
/// Determine image type from base64
fn from_str(from: &str) -> Result<Self, Self::Err>
{
let from = from.as_bytes();
Self::from_base64(from)
}
}
/// Calculate the required data size from base64 input size
pub const fn data_size(base64: usize) -> usize
{
((4 * base64 / 3) + 3) & !3
}
#[inline]
fn find(haystack: &[u8], needle: &[u8]) -> Option<usize> {
haystack.windows(needle.len()).position(|window| window == needle)
}
#[inline]
fn find_back(haystack: &[u8], needle: &[u8]) -> Option<usize> {
haystack.windows(needle.len()).rev().position(|window| window == needle).and_then(|v| Some(haystack.len() - v))
}
const MARKER_BASE64_BEGIN: &[u8] = b"base64,";
const MARKER_BASE64_END: &[u8] = b"' /><p"; //Search from end here with .rev()
const MARKER_TAGS_BEGIN: &[u8] = br"<p id='tags'>";
const MARKER_TAGS_END: &[u8] = br"</p><a id='about'";
/// Find the base64 page bounds in this array
pub(super) fn find_bounds(from: impl AsRef<[u8]>) -> Result<Range<usize>, error::DecodeError>
{
let from = from.as_ref();
if let Some(start) = find(from, MARKER_BASE64_BEGIN) {
let start = start + MARKER_BASE64_BEGIN.len();
if let Some(end) = find_back(&from[start..], MARKER_BASE64_END) {//find_back(from, MARKER_BASE64_END) {
let end = end - MARKER_BASE64_END.len();
Ok(Range {
start,
end: end + start,
})
} else {
Err(error::DecodeError::Bounds(error::Bound::End))
}
} else {
Err(error::DecodeError::Bounds(error::Bound::Start))
}
}
/// Find the tag bounds in this array
// We should pass this a slice from `base64bounds.end..`
pub(super) fn find_tag_bounds(from: impl AsRef<[u8]>) -> Result<Range<usize>, error::DecodeError>
{
let from = from.as_ref();
if let Some(start) = find(from, MARKER_TAGS_BEGIN) {
let start = start + MARKER_TAGS_BEGIN.len();
if let Some(end) = find_back(&from[start..], MARKER_TAGS_END) {//find_back(from, MARKER_TAGS_END) {
let end = end - MARKER_TAGS_END.len();
Ok(Range {
start,
end: end + start,
})
} else {
Err(error::DecodeError::Bounds(error::Bound::End))
}
} else {
Err(error::DecodeError::Bounds(error::Bound::Start))
}
}