use std::io::{Read, Write};
use std::slice::{Iter, IterMut};
use flate2::write::GzEncoder;
use flate2::{read::GzDecoder, Compression};
use crate::BufferIterator;
use crate::KOSValue;
use crate::{ksm::sections::CodeSection, FromBytes, ToBytes};
pub mod builder;
pub use builder::*;
pub mod errors;
pub mod sections;
use sections::{ArgumentSection, DebugSection};
pub mod instructions;
use crate::ksm::errors::{HeaderParseError, KSMParseError};
use crate::ksm::sections::{ArgIndex, DebugEntry};
pub use instructions::Instr;
const KSM_MAGIC_NUMBER: u32 = 0x4558036b;
#[derive(Debug, Clone)]
pub struct KSMFile {
pub header: KSMHeader,
pub arg_section: ArgumentSection,
pub debug_section: DebugSection,
code_sections: Vec<CodeSection>,
}
impl KSMFile {
pub fn new_from_parts(
arg_section: ArgumentSection,
code_sections: Vec<CodeSection>,
debug_section: DebugSection,
) -> Self {
Self {
header: KSMHeader::new(),
arg_section,
code_sections,
debug_section,
}
}
pub fn with_code_section(mut self, code_section: CodeSection) -> Self {
self.code_sections.push(code_section);
self
}
pub fn with_code_sections(
mut self,
code_sections: impl IntoIterator<Item = CodeSection>,
) -> Self {
self.code_sections.extend(code_sections);
self
}
pub fn add_argument(&mut self, argument: KOSValue) -> ArgIndex {
self.arg_section.add(argument)
}
pub fn add_debug_entry(&mut self, entry: DebugEntry) {
self.debug_section.add(entry)
}
pub fn code_sections(&self) -> Iter<CodeSection> {
self.code_sections.iter()
}
pub fn code_sections_mut(&mut self) -> IterMut<CodeSection> {
self.code_sections.iter_mut()
}
pub fn add_code_section(&mut self, code_section: CodeSection) {
self.code_sections.push(code_section);
}
pub fn parse(source: &mut BufferIterator) -> Result<Self, KSMParseError> {
let source_len = source.len();
let mut decoder = GzDecoder::new(source);
let mut decompressed: Vec<u8> = Vec::with_capacity(source_len);
decoder
.read_to_end(&mut decompressed)
.map_err(KSMParseError::DecompressionError)?;
let mut decompressed_source = BufferIterator::new(&decompressed);
let header =
KSMHeader::parse(&mut decompressed_source).map_err(KSMParseError::HeaderError)?;
let arg_section = ArgumentSection::parse(&mut decompressed_source)
.map_err(KSMParseError::ArgumentSectionParseError)?;
let mut code_sections = Vec::new();
loop {
assert_eq!(decompressed_source.next().unwrap(), b'%');
let next = decompressed_source.peek().ok_or_else(|| {
KSMParseError::MissingSectionType(decompressed_source.current_index())
})?;
if next == b'D' {
break;
}
let code_section =
CodeSection::parse(&mut decompressed_source, arg_section.num_index_bytes())
.map_err(KSMParseError::CodeSectionParseError)?;
code_sections.push(code_section);
}
let debug_section = DebugSection::parse(&mut decompressed_source)
.map_err(KSMParseError::DebugSectionParseError)?;
Ok(Self {
header,
arg_section,
code_sections,
debug_section,
})
}
pub fn write(&self, buf: &mut Vec<u8>) {
let mut uncompressed_buf = Vec::with_capacity(2048);
let mut zipped_contents = Vec::with_capacity(2048);
self.header.write(&mut uncompressed_buf);
self.arg_section.write(&mut uncompressed_buf);
let mut encoder = GzEncoder::new(&mut zipped_contents, Compression::best());
for code_section in self.code_sections.iter() {
code_section.write(&mut uncompressed_buf, self.arg_section.num_index_bytes());
}
self.debug_section.write(&mut uncompressed_buf);
encoder
.write_all(&uncompressed_buf)
.expect("Error compressing KSM file");
encoder
.finish()
.expect("Error finishing KSM file compression");
buf.append(&mut zipped_contents);
}
}
#[derive(Debug, Copy, Clone)]
pub struct KSMHeader {
magic: u32,
}
impl KSMHeader {
const HEADER_SIZE: usize = 4;
pub const fn new() -> Self {
Self {
magic: KSM_MAGIC_NUMBER,
}
}
pub fn parse(source: &mut BufferIterator) -> Result<Self, HeaderParseError> {
let magic = u32::from_bytes(source).map_err(|_: ()| HeaderParseError::EOF)?;
(magic == KSM_MAGIC_NUMBER)
.then_some(Self::new())
.ok_or(HeaderParseError::InvalidMagic(magic))
}
pub fn write(&self, buf: &mut Vec<u8>) {
self.magic.to_bytes(buf)
}
pub const fn size_bytes(&self) -> usize {
Self::HEADER_SIZE
}
}
impl Default for KSMHeader {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(u8)]
pub enum IntSize {
One = 1,
Two = 2,
Three = 3,
Four = 4,
}
impl TryFrom<u8> for IntSize {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::One),
2 => Ok(Self::Two),
3 => Ok(Self::Three),
4 => Ok(Self::Four),
_ => Err(value),
}
}
}
impl From<IntSize> for u8 {
fn from(num: IntSize) -> Self {
num as u8
}
}
pub(crate) fn read_var_int(source: &mut BufferIterator, width: IntSize) -> Result<u32, ()> {
match width {
IntSize::One => u8::from_bytes(source).map(|i| i.into()),
IntSize::Two => {
let mut slice = [0u8; 2];
for b in &mut slice {
*b = source.next().ok_or(())?;
}
Ok(u16::from_be_bytes(slice).into())
}
IntSize::Three => {
let mut slice = [0u8; 4];
for b in &mut slice[1..4] {
*b = source.next().ok_or(())?;
}
Ok(u32::from_be_bytes(slice))
}
IntSize::Four => {
let mut slice = [0u8; 4];
for b in &mut slice {
*b = source.next().ok_or(())?;
}
Ok(u32::from_be_bytes(slice))
}
}
}
pub(crate) fn write_var_int(value: u32, buf: &mut Vec<u8>, width: IntSize) {
match width {
IntSize::One => {
(value as u8).to_bytes(buf);
}
IntSize::Two => {
buf.extend_from_slice(&(value as u16).to_be_bytes());
}
IntSize::Three => {
let slice = &value.to_be_bytes();
buf.extend_from_slice(&slice[1..4]);
}
IntSize::Four => {
buf.extend_from_slice(&value.to_be_bytes());
}
}
}
pub(crate) fn fewest_bytes_to_hold(value: u32) -> IntSize {
if value <= u8::MAX as u32 {
IntSize::One
} else if value <= u16::MAX as u32 {
IntSize::Two
} else if value <= 1677215u32 {
IntSize::Three
} else {
IntSize::Four
}
}
#[cfg(test)]
impl PartialEq for KSMFile {
fn eq(&self, other: &Self) -> bool {
if self.arg_section != other.arg_section {
return false;
}
if self.debug_section != other.debug_section {
return false;
}
for (value1, value2) in self.code_sections.iter().zip(other.code_sections.iter()) {
if value1 != value2 {
return false;
}
}
true
}
}