Skip to content

Commit

Permalink
Merge pull request #133 from rakaly/rgba
Browse files Browse the repository at this point in the history
Support binary rgb values with optional alpha component
  • Loading branch information
nickbabcock authored Nov 21, 2023
2 parents bfb8530 + f1da843 commit 249046a
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 33 deletions.
44 changes: 39 additions & 5 deletions src/binary/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,20 @@ impl<'data> OndemandParser<'data> {
let g = self.read_u32()?;
let btoken = self.read()?;
let b = self.read_u32()?;
let end = self.read()?;
match (start, rtoken, gtoken, btoken, end) {
(OPEN, U32, U32, U32, END) => Ok(Rgb { r, g, b }),
_ => Err(self.invalid_syntax("invalid rgb value")),
}
let next_tok = self.read()?;
let a = match (start, rtoken, gtoken, btoken, next_tok) {
(OPEN, U32, U32, U32, END) => None,
(OPEN, U32, U32, U32, U32) => {
let a = Some(self.read_u32()?);
if self.read()? != END {
return Err(self.invalid_syntax("expected end after rgb alpha"));
}
a
}
_ => return Err(self.invalid_syntax("invalid rgb value")),
};

Ok(Rgb { r, g, b, a })
}

#[cold]
Expand Down Expand Up @@ -2718,6 +2727,31 @@ mod tests {
}
}

#[test]
fn test_deserialize_rgba() {
let data = [
0x3a, 0x05, 0x01, 0x00, 0x43, 0x02, 0x03, 0x00, 0x14, 0x00, 0x6e, 0x00, 0x00, 0x00,
0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x14, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x04, 0x00,
];

let mut map = HashMap::new();
map.insert(0x053a, "color");

let actual: MyStruct = from_slice(&data[..], &map).unwrap();
assert_eq!(
actual,
MyStruct {
color: (String::from("rgb"), (110, 27, 27, 28))
}
);

#[derive(Deserialize, Debug, PartialEq)]
struct MyStruct {
color: (String, (u8, u8, u8, u8)),
}
}

#[test]
fn test_object_len() {
let tokens = vec![
Expand Down
3 changes: 3 additions & 0 deletions src/binary/rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ pub struct Rgb {

/// Blue channel
pub b: u32,

/// Optional alpha channel
pub a: Option<u32>,
}
82 changes: 68 additions & 14 deletions src/binary/tape.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use super::tokens::*;
use crate::{
binary::Rgb,
copyless::VecHelper,
util::{get_split, le_u32},
Error, ErrorKind, Scalar,
};
use crate::{binary::Rgb, copyless::VecHelper, util::get_split, Error, ErrorKind, Scalar};

/// Represents any valid binary value
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -214,14 +209,42 @@ impl<'a, 'b> ParserState<'a, 'b> {
}

fn parse_rgb(&mut self, data: &'a [u8]) -> Result<&'a [u8], Error> {
let (head, rest) = get_split::<22>(data).ok_or_else(Error::eof)?;
let val = Rgb {
r: le_u32(&head[4..]),
g: le_u32(&head[10..]),
b: le_u32(&head[16..]),
let data = &data[2..];
let (data, r_tok) = self.parse_next_id(data)?;
let (r_data, data) = get_split::<4>(data).ok_or_else(Error::eof)?;
let r = u32::from_le_bytes(r_data);

let (data, g_tok) = self.parse_next_id(data)?;
let (g_data, data) = get_split::<4>(data).ok_or_else(Error::eof)?;
let g = u32::from_le_bytes(g_data);

let (data, b_tok) = self.parse_next_id(data)?;
let (b_data, data) = get_split::<4>(data).ok_or_else(Error::eof)?;
let b = u32::from_le_bytes(b_data);

if r_tok != U32 && g_tok != U32 && b_tok != U32 {
return Err(self.invalid_syntax("invalid rgb tokens", data));
}

let (data, next_tok) = self.parse_next_id(data)?;

let (data, a) = match next_tok {
U32 => {
let (a_data, data) = get_split::<4>(data).ok_or_else(Error::eof)?;
let a = u32::from_le_bytes(a_data);
let (data, end_tok) = self.parse_next_id(data)?;
if end_tok != END {
return Err(self.invalid_syntax("expected end to follow rgb alpha", data));
}
(data, Some(a))
}
END => (data, None),
_ => return Err(self.invalid_syntax("invalid rgb end token", data)),
};

let val = Rgb { r, g, b, a };
self.token_tape.alloc().init(BinaryToken::Rgb(val));
Ok(rest)
Ok(data)
}

#[inline(always)]
Expand Down Expand Up @@ -748,6 +771,15 @@ impl<'a, 'b> ParserState<'a, 'b> {
offset: self.offset(data),
})
}

#[inline(never)]
#[cold]
fn invalid_syntax<T: Into<String>>(&self, msg: T, data: &[u8]) -> Error {
Error::new(ErrorKind::InvalidSyntax {
msg: msg.into(),
offset: self.offset(data),
})
}
}

/// Houses the tape of tokens that is extracted from binary data
Expand Down Expand Up @@ -784,9 +816,7 @@ mod tests {
#[test]
fn test_size_of_binary_token() {
let token_size = std::mem::size_of::<BinaryToken>();
let maxed = std::cmp::max(std::mem::size_of::<Scalar>(), std::mem::size_of::<Rgb>());
assert!(token_size <= 24);
assert_eq!(token_size, maxed + std::mem::size_of::<usize>());
}

#[test]
Expand Down Expand Up @@ -1199,6 +1229,7 @@ mod tests {
r: 110,
g: 27,
b: 27,
a: None
})
]
);
Expand All @@ -1214,6 +1245,29 @@ mod tests {
);
}

#[test]
fn test_rgba() {
let data = [
0x3a, 0x05, 0x01, 0x00, 0x43, 0x02, 0x03, 0x00, 0x14, 0x00, 0x6e, 0x00, 0x00, 0x00,
0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x14, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x04, 0x00,
];

let tape = parse(&data[..]).unwrap();
assert_eq!(
tape.token_tape,
vec![
BinaryToken::Token(0x053a),
BinaryToken::Rgb(Rgb {
r: 110,
g: 27,
b: 27,
a: Some(28)
})
]
);
}

#[test]
fn test_u64() {
let data = [
Expand Down
3 changes: 2 additions & 1 deletion src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl InnerColorSequence {
1 => self.data.r,
2 => self.data.g,
3 => self.data.b,
4 => self.data.a.unwrap(),
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -96,7 +97,7 @@ impl<'de> SeqAccess<'de> for InnerColorSequence {
where
T: DeserializeSeed<'de>,
{
if self.idx >= 3 {
if (self.idx >= 3 && self.data.a.is_none()) || (self.idx >= 4 && self.data.a.is_some()) {
Ok(None)
} else {
self.idx += 1;
Expand Down
40 changes: 32 additions & 8 deletions src/text/writer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
binary::Rgb,
common::PdsDateFormatter,
text::{ArrayReader, ObjectReader, Operator, ValueReader},
BinaryToken, Encoding, Error, ErrorKind, TextTape, TextToken,
Expand Down Expand Up @@ -444,6 +445,36 @@ where
write!(self, "{}", data)
}

/// Write an rgb value
///
/// ```
/// use jomini::{binary::Rgb, TextWriterBuilder};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut out: Vec<u8> = Vec::new();
/// let mut writer = TextWriterBuilder::new().from_writer(&mut out);
/// writer.write_unquoted(b"start")?;
/// let val = Rgb { r: 10, g: 9, b: 8, a: None };
/// writer.write_rgb(&val)?;
/// writer.write_unquoted(b"end")?;
///
/// let val = Rgb { r: 7, g: 6, b: 5, a: Some(4) };
/// writer.write_rgb(&val)?;
/// assert_eq!(&out, b"start=rgb {\n 10 9 8\n}\nend=rgb {\n 7 6 5 4\n}");
/// # Ok(())
/// # }
/// ```
pub fn write_rgb(&mut self, color: &Rgb) -> Result<(), Error> {
self.write_header(b"rgb")?;
self.write_array_start()?;
self.write_u32(color.r)?;
self.write_u32(color.g)?;
self.write_u32(color.b)?;
if let Some(a) = color.a {
self.write_u32(a)?;
}
self.write_end()
}

/// Write formatted data
///
/// Typically not invoked directly but instead through the `write!` macro
Expand Down Expand Up @@ -588,14 +619,7 @@ where
BinaryToken::Token(x) => {
write!(self, "__unknown_0x{:x}", x)
}
BinaryToken::Rgb(color) => {
self.write_header(b"rgb")?;
self.write_array_start()?;
self.write_u32(color.r)?;
self.write_u32(color.g)?;
self.write_u32(color.b)?;
self.write_end()
}
BinaryToken::Rgb(color) => self.write_rgb(color),
}
}

Expand Down
5 changes: 0 additions & 5 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ pub(crate) const fn fast_digit_parse(val: u64) -> Option<u64> {
}
}

#[inline]
pub(crate) fn le_u32(data: &[u8]) -> u32 {
u32::from_le_bytes(take::<4>(data))
}

#[inline]
pub(crate) fn le_u64(data: &[u8]) -> u64 {
u64::from_le_bytes(take::<8>(data))
Expand Down

0 comments on commit 249046a

Please sign in to comment.