Skip to content

Instantly share code, notes, and snippets.

@irwins
Last active January 9, 2024 17:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save irwins/06fd11510f5ad64d64badefba8af822f to your computer and use it in GitHub Desktop.
Save irwins/06fd11510f5ad64d64badefba8af822f to your computer and use it in GitHub Desktop.
PowerShell RSV
#region functions
function Encode-Rsv {
param (
[Parameter(Mandatory=$true)]
[Array]$rows
)
$parts = @()
foreach ($row in $rows) {
foreach ($value in $row) {
if ($null -eq $value) {
$parts += [byte]0xFE
} elseif ($value.Length -gt 0) {
$parts += [System.Text.Encoding]::UTF8.GetBytes($value)
}
$parts += [byte]0xFF
}
$parts += [byte]0xFD
}
return [System.Byte[]]$parts
}
function Decode-Rsv {
param (
[byte[]]$bytes
)
if ($bytes.Length -gt 0 -and $bytes[-1] -ne 0xFD) {
throw "Incomplete RSV document"
}
$result = @()
$current_row = @()
$value_start_index = 0
for ($i = 0; $i -lt $bytes.Length; $i++) {
if ($bytes[$i] -eq 0xFF) {
$length = $i - $value_start_index
if ($length -eq 0) {
$current_row += ""
#Write-Output "Empty string $($current_row)"
}
elseif ($length -eq 1 -and $bytes[$value_start_index] -eq 0xFE) {
$current_row += $null
#Write-Output "Null $($current_row)"
}
else {
$value_bytes = $bytes[$value_start_index..($i - 1)]
$current_row += [System.Text.Encoding]::Default.GetString($value_bytes)
#Write-Output "String $($current_row)"
}
$value_start_index = $i + 1
}
elseif ($bytes[$i] -eq 0xFD) {
if ($i -gt 0 -and $value_start_index -ne $i) {
throw "Incomplete RSV row"
}
#Write-Output $current_row
$result += @(,$current_row)
$current_row = @()
$value_start_index = $i + 1
}
}
return $result
}
#endregion
#region main
#region Short test
$rows = @(
@("Hello", "🌎", $null, ""),
@("A\0B\nC", "Test π„ž"),
@(),
@("")
)
$rowsToJson = $rows | ConvertTo-Json -Compress -Depth 100 -EscapeHandling Default
$rowsToJson
#$rowsToJson Output is: [["Hello","🌎",null,""],["A\0B\nC","Test π„ž"],[],[""]]
#endregion
#Encode
$encodedBytes = Encode-Rsv -rows $rows
$encodedBytes
#Save to rsv file
$rsvFilePath = "rsv/playground/test.rsv"
[System.IO.File]::WriteAllBytes($rsvFilePath, $encodedBytes)
#load from rsv file
$loadFromFile = [System.IO.File]::ReadAllBytes($rsvFilePath)
$loadFromFile
$decodedBytes = Decode-Rsv -bytes $loadFromFile
$decodedBytes | ConvertTo-Json -Compress -Depth 100 -EscapeHandling Default
#$decodedBytes Output is: [["Hello","🌎",null,""],["A\0B\nC","Test π„ž"],[],[""]]
#endregion
use std::fs::File;
use std::io::prelude::*;
fn encode_rsv(rows: &[Vec<Option<&str>>]) -> Vec<u8> {
let mut parts: Vec<u8> = Vec::new();
for row in rows {
for value in row {
match value {
None => parts.push(0xFE),
Some(v) => {
if !v.is_empty() {
parts.extend_from_slice(v.as_bytes());
}
}
}
parts.push(0xFF);
}
parts.push(0xFD);
}
parts
}
fn decode_rsv(bytes: &[u8]) -> Vec<Vec<Option<String>>> {
let mut result: Vec<Vec<Option<String>>> = Vec::new();
let mut current_row: Vec<Option<String>> = Vec::new();
let mut value_start_index = 0;
for (i, &byte) in bytes.iter().enumerate() {
if byte == 0xFF {
let length = i - value_start_index;
if length == 0 {
current_row.push(None);
} else if length == 1 && bytes[value_start_index] == 0xFE {
current_row.push(None);
} else {
let value_bytes = &bytes[value_start_index..i];
let value = String::from_utf8_lossy(value_bytes).to_string();
current_row.push(Some(value));
}
value_start_index = i + 1;
} else if byte == 0xFD {
if i > 0 && value_start_index != i {
panic!("Incomplete RSV row");
}
result.push(current_row);
current_row = Vec::new();
value_start_index = i + 1;
}
}
result
}
fn main() {
let rows: Vec<Vec<Option<&str>>> = vec![
vec!["Hello", "🌎", None, ""],
vec!["A\0B\nC", "Test π„ž"],
vec![],
vec![""],
];
// Encode
let encoded_bytes = encode_rsv(&rows);
println!("{:?}", encoded_bytes);
// Save to RSV file
let rsv_file_path = "rsv/playground/test.rsv";
let mut file = File::create(rsv_file_path).expect("Failed to create file");
file.write_all(&encoded_bytes).expect("Failed to write to file");
// Load from RSV file
let mut load_from_file = Vec::new();
let mut file = File::open(rsv_file_path).expect("Failed to open file");
file.read_to_end(&mut load_from_file).expect("Failed to read from file");
let decoded_bytes = decode_rsv(&load_from_file);
println!("{:?}", decoded_bytes);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment