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.
141 lines
3.4 KiB
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))
|
|
}
|
|
}
|