Initial Commit
This commit is contained in:
commit
4b165a6736
9 changed files with 412 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "b32_rs"
|
||||
version = "0.1.0"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "b32_rs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
91
src/decode.rs
Normal file
91
src/decode.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use std::io::{self, Read, Write};
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DecoderError {
|
||||
IO(io::Error),
|
||||
IllegalChar,
|
||||
}
|
||||
|
||||
impl From<io::Error> for DecoderError {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::IO(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Decoder<R: Read, W: Write> {
|
||||
reader: R,
|
||||
writer: W,
|
||||
alphabet: B32Alphabet,
|
||||
}
|
||||
|
||||
impl<R: Read, W: Write> Decoder<R, W> {
|
||||
pub fn new(reader: R, writer: W, alphabet: Option<B32Alphabet>) -> Self {
|
||||
Self { reader, writer, alphabet: alphabet.unwrap_or_default() }
|
||||
}
|
||||
|
||||
pub fn decode(&mut self) -> Result<(), DecoderError> {
|
||||
let mut buf = [0; 8];
|
||||
let mut num: u64 = 0;
|
||||
'outer: loop {
|
||||
if let Err(e) = self.reader.read_exact(&mut buf) {
|
||||
if e.kind() == io::ErrorKind::UnexpectedEof {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for c in &buf {
|
||||
let c = char::from(*c);
|
||||
num <<= 5;
|
||||
if !matches!(self.alphabet.pad(), Some(ch) if ch == c) {
|
||||
let idx = self.alphabet.idx(c).ok_or(DecoderError::IllegalChar)?;
|
||||
num |= idx as u64;
|
||||
}
|
||||
}
|
||||
let mut buf = [0; 5];
|
||||
for i in (0..5).rev() {
|
||||
let b = (num & 0xff) as u8;
|
||||
buf[i] = b;
|
||||
num >>= 8;
|
||||
}
|
||||
for c in &buf {
|
||||
if *c == b'\0' {
|
||||
break 'outer;
|
||||
} else {
|
||||
self.writer.write_all(&[*c])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.writer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Decoder<R, Vec<u8>> {
|
||||
pub fn bytes(self) -> Vec<u8> {
|
||||
self.writer
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
static HELLO: &'static str = "Hello, World!";
|
||||
static ENCODED: &'static str = "JBSWY3DPFQQFO33SNRSCC===";
|
||||
|
||||
#[test]
|
||||
fn get_idx() {
|
||||
let idx = B32_RFC4648_ALPHABET.idx('S').unwrap();
|
||||
assert_eq!(idx, 18);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello() {
|
||||
let reader = ENCODED.as_bytes();
|
||||
let writer = Vec::<u8>::new();
|
||||
let mut decoder = Decoder::new(reader, writer, None);
|
||||
decoder.decode().unwrap();
|
||||
assert_eq!(HELLO, String::from_utf8(decoder.bytes()).unwrap());
|
||||
}
|
||||
}
|
||||
|
153
src/encode.rs
Normal file
153
src/encode.rs
Normal file
|
@ -0,0 +1,153 @@
|
|||
use {
|
||||
super::*,
|
||||
std::{
|
||||
fmt::{self, Write},
|
||||
io::{self, Read},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EncoderError {
|
||||
IO(io::Error),
|
||||
FMT(fmt::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for EncoderError {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::IO(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for EncoderError {
|
||||
fn from(value: fmt::Error) -> Self {
|
||||
Self::FMT(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Encoder<R: Read, W: Write> {
|
||||
reader: R,
|
||||
writer: W,
|
||||
alphabet: B32Alphabet,
|
||||
wrap: Option<usize>,
|
||||
}
|
||||
|
||||
impl<R: Read, W: Write> Encoder<R, W> {
|
||||
pub fn new(reader: R, writer: W, alphabet: Option<B32Alphabet>, wrap: Option<usize>) -> Self {
|
||||
Self {
|
||||
reader,
|
||||
writer,
|
||||
alphabet: alphabet.unwrap_or_default(),
|
||||
wrap,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&mut self) -> Result<(), EncoderError> {
|
||||
let mut total: usize = 0;
|
||||
loop {
|
||||
let mut buf = [0; 5];
|
||||
let mut num: u64 = 0;
|
||||
let n_bytes;
|
||||
loop {
|
||||
n_bytes = match self.reader.read(&mut buf) {
|
||||
Ok(n) => n,
|
||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
break;
|
||||
}
|
||||
if n_bytes == 0 {
|
||||
break;
|
||||
}
|
||||
for (idx, c) in buf.iter().enumerate() {
|
||||
num <<= 8;
|
||||
if idx < n_bytes {
|
||||
num |= *c as u64;
|
||||
}
|
||||
}
|
||||
let mut buf = ['\0'; 8];
|
||||
for i in (0..8).rev() {
|
||||
let b = (num & 0x1f) as u8;
|
||||
buf[i] = char::try_from(self.alphabet.items[b as usize]).unwrap();
|
||||
num >>= 5;
|
||||
}
|
||||
let mut outlen = n_bytes * 8 / 5;
|
||||
let rem = n_bytes * 8 % 5;
|
||||
if rem != 0 {
|
||||
outlen += 1;
|
||||
}
|
||||
buf[0..outlen].iter().try_for_each(|c| {
|
||||
self.writer.write_char(*c)?;
|
||||
total += 1;
|
||||
if let Some(wrap) = self.wrap {
|
||||
if total % wrap == 0 {
|
||||
self.writer.write_char('\n')?;
|
||||
}
|
||||
}
|
||||
Ok::<(), fmt::Error>(())
|
||||
})?;
|
||||
if let Some(pad) = self.alphabet.pad {
|
||||
if rem != 0 {
|
||||
for _c in 0..8 - outlen {
|
||||
self.writer.write_char(char::try_from(pad).unwrap())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if n_bytes < 5 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Encoder<R, String> {
|
||||
pub fn output(self) -> String {
|
||||
self.writer
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
std::{
|
||||
fs::{self, File},
|
||||
io::BufReader,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn hello() {
|
||||
let reader = "Hello, World".as_bytes();
|
||||
let writer = String::new();
|
||||
let mut encoder = Encoder::new(reader, writer, None, None);
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), "JBSWY3DPFQQFO33SNRSA====");
|
||||
encoder = Encoder::new(
|
||||
"Hello, World!".as_bytes(),
|
||||
String::new(),
|
||||
None,
|
||||
None
|
||||
);
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), "JBSWY3DPFQQFO33SNRSCC===");
|
||||
encoder = Encoder::new(
|
||||
"Hello, World!\n".as_bytes(),
|
||||
String::new(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), "JBSWY3DPFQQFO33SNRSCCCQ=");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_from_file() {
|
||||
let infile = File::open("src/testdata/lorem.txt").unwrap();
|
||||
let reader = BufReader::new(infile);
|
||||
let outfile = fs::read_to_string("src/testdata/lorem_b32.txt").unwrap();
|
||||
let mut encoder = Encoder::new(reader, String::new(), None, Some(76));
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), outfile);
|
||||
}
|
||||
}
|
57
src/error.rs
Normal file
57
src/error.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use std::{error, fmt, io, num::TryFromIntError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Fmt(fmt::Error),
|
||||
Io(io::Error),
|
||||
IllegalChar(char),
|
||||
IntError(TryFromIntError),
|
||||
MissingPadding,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Fmt(e) => write!(f, "{e}"),
|
||||
Self::Io(e) => write!(f, "{e}"),
|
||||
Self::IllegalChar(c) => write!(f, "Illegal Character ({c})"),
|
||||
Self::MissingPadding => write!(f, "Missing Padding"),
|
||||
Self::IntError(e) => write!(f, "Int Error: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for Error {
|
||||
fn from(value: fmt::Error) -> Self {
|
||||
Self::Fmt(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::Io(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Error {
|
||||
fn from(value: char) -> Self {
|
||||
Self::IllegalChar(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for Error {
|
||||
fn from(value: TryFromIntError) -> Self {
|
||||
Self::IntError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::Fmt(e) => Some(e),
|
||||
Self::Io(e) => Some(e),
|
||||
Self::IntError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
39
src/lib.rs
Normal file
39
src/lib.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
#[warn(clippy::all, clippy::pedantic)]
|
||||
pub mod decode;
|
||||
pub mod encode;
|
||||
pub mod error;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct B32Alphabet {
|
||||
items: [char; 32],
|
||||
pad: Option<char>,
|
||||
}
|
||||
|
||||
pub static B32_RFC4648_ALPHABET: B32Alphabet = B32Alphabet {
|
||||
items: [
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||
],
|
||||
pad: Some('='),
|
||||
};
|
||||
|
||||
impl Default for B32Alphabet {
|
||||
fn default() -> Self {
|
||||
B32_RFC4648_ALPHABET
|
||||
}
|
||||
}
|
||||
|
||||
impl B32Alphabet {
|
||||
pub(crate) fn idx(&self, c: char) -> Option<usize> {
|
||||
for (idx, x) in self.items.iter().enumerate() {
|
||||
if *x == c {
|
||||
return Some(idx);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn pad(&self) -> Option<char> {
|
||||
self.pad
|
||||
}
|
||||
}
|
9
src/testdata/lorem.txt
vendored
Normal file
9
src/testdata/lorem.txt
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
Lorem ipsum odor amet, consectetuer adipiscing elit. Hendrerit eu semper lectus amet est proin. Tempus himenaeos at fusce lacus habitasse sociosqu torquent varius arcu. Odio netus fermentum nisl natoque per cubilia himenaeos urna. Elit venenatis eros risus vulputate duis interdum. Mauris dis ad pharetra dui aptent. Imperdiet orci auctor condimentum adipiscing arcu. Nullam tempor magna odio volutpat nisi. Condimentum scelerisque risus faucibus; duis sollicitudin nullam.
|
||||
|
||||
Condimentum ex fringilla tempus ultricies consectetur odio egestas venenatis vulputate. Sem sapien pellentesque ornare; viverra scelerisque consequat. Platea posuere mi senectus felis dolor dictumst mattis ultricies est. Malesuada elit adipiscing suspendisse est a. Justo luctus turpis tempus leo finibus rutrum nostra magnis aliquam. Felis posuere molestie praesent porta justo nisi. Etiam aptent conubia consectetur dis; penatibus elementum ut.
|
||||
|
||||
Morbi nostra netus ad nunc egestas neque nascetur fusce. Consequat ultrices scelerisque ut rhoncus volutpat, hendrerit convallis massa. Tortor himenaeos nullam malesuada suspendisse consectetur potenti volutpat. In suscipit sit maximus; lacinia eu nulla porta. Tempus commodo libero auctor cras bibendum sit. Metus aptent mauris nascetur venenatis diam metus. Inceptos mi ultrices class morbi donec. Orci pulvinar risus accumsan faucibus litora duis commodo commodo.
|
||||
|
||||
Aliquam etiam habitasse fames massa, hendrerit purus interdum nullam. Curabitur placerat tortor placerat nec adipiscing habitasse. Massa litora interdum pretium fusce nascetur primis faucibus magna. Dui curabitur molestie justo ullamcorper leo molestie mauris. Inceptos malesuada taciti litora libero maecenas neque nam laoreet. Tristique vivamus eget maecenas; porta fames curae mollis cras. Ipsum vulputate ullamcorper et nascetur nascetur vel orci.
|
||||
|
||||
Curae et odio; urna eros pulvinar malesuada eget dignissim. Cursus fermentum lectus phasellus tempor posuere vulputate quam. Mauris commodo commodo mauris inceptos metus varius urna sagittis. Ligula duis erat ipsum pretium eu facilisis. Duis lobortis proin facilisi ad suscipit vestibulum hac tortor interdum. Ultrices tincidunt maximus aptent phasellus in ullamcorper nisl varius dis. Venenatis enim potenti potenti sodales massa id elementum.
|
49
src/testdata/lorem_b32.txt
vendored
Normal file
49
src/testdata/lorem_b32.txt
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
JRXXEZLNEBUXA43VNUQG6ZDPOIQGC3LFOQWCAY3PNZZWKY3UMV2HKZLSEBQWI2LQNFZWG2LOM4QG
|
||||
K3DJOQXCASDFNZSHEZLSNF2CAZLVEBZWK3LQMVZCA3DFMN2HK4ZAMFWWK5BAMVZXIIDQOJXWS3RO
|
||||
EBKGK3LQOVZSA2DJNVSW4YLFN5ZSAYLUEBTHK43DMUQGYYLDOVZSA2DBMJUXIYLTONSSA43PMNUW
|
||||
643ROUQHI33SOF2WK3TUEB3GC4TJOVZSAYLSMN2S4ICPMRUW6IDOMV2HK4ZAMZSXE3LFNZ2HK3JA
|
||||
NZUXG3BANZQXI33ROVSSA4DFOIQGG5LCNFWGSYJANBUW2ZLOMFSW64ZAOVZG4YJOEBCWY2LUEB3G
|
||||
K3TFNZQXI2LTEBSXE33TEBZGS43VOMQHM5LMOB2XIYLUMUQGI5LJOMQGS3TUMVZGI5LNFYQE2YLV
|
||||
OJUXGIDENFZSAYLEEBYGQYLSMV2HEYJAMR2WSIDBOB2GK3TUFYQES3LQMVZGI2LFOQQG64TDNEQG
|
||||
C5LDORXXEIDDN5XGI2LNMVXHI5LNEBQWI2LQNFZWG2LOM4QGC4TDOUXCATTVNRWGC3JAORSW24DP
|
||||
OIQG2YLHNZQSA33ENFXSA5TPNR2XI4DBOQQG42LTNEXCAQ3PNZSGS3LFNZ2HK3JAONRWK3DFOJUX
|
||||
G4LVMUQHE2LTOVZSAZTBOVRWSYTVOM5SAZDVNFZSA43PNRWGSY3JOR2WI2LOEBXHK3DMMFWS4CQK
|
||||
INXW4ZDJNVSW45DVNUQGK6BAMZZGS3THNFWGYYJAORSW24DVOMQHK3DUOJUWG2LFOMQGG33OONSW
|
||||
G5DFOR2XEIDPMRUW6IDFM5SXG5DBOMQHMZLOMVXGC5DJOMQHM5LMOB2XIYLUMUXCAU3FNUQHGYLQ
|
||||
NFSW4IDQMVWGYZLOORSXG4LVMUQG64TOMFZGKOZAOZUXMZLSOJQSA43DMVWGK4TJONYXKZJAMNXW
|
||||
443FOF2WC5BOEBIGYYLUMVQSA4DPON2WK4TFEBWWSIDTMVXGKY3UOVZSAZTFNRUXGIDEN5WG64RA
|
||||
MRUWG5DVNVZXIIDNMF2HI2LTEB2WY5DSNFRWSZLTEBSXG5BOEBGWC3DFON2WCZDBEBSWY2LUEBQW
|
||||
I2LQNFZWG2LOM4QHG5LTOBSW4ZDJONZWKIDFON2CAYJOEBFHK43UN4QGY5LDOR2XGIDUOVZHA2LT
|
||||
EB2GK3LQOVZSA3DFN4QGM2LONFRHK4ZAOJ2XI4TVNUQG433TORZGCIDNMFTW42LTEBQWY2LROVQW
|
||||
2LRAIZSWY2LTEBYG643VMVZGKIDNN5WGK43UNFSSA4DSMFSXGZLOOQQHA33SORQSA2TVON2G6IDO
|
||||
NFZWSLRAIV2GSYLNEBQXA5DFNZ2CAY3PNZ2WE2LBEBRW63TTMVRXIZLUOVZCAZDJOM5SA4DFNZQX
|
||||
I2LCOVZSAZLMMVWWK3TUOVWSA5LUFYFAUTLPOJRGSIDON5ZXI4TBEBXGK5DVOMQGCZBANZ2W4YZA
|
||||
MVTWK43UMFZSA3TFOF2WKIDOMFZWGZLUOVZCAZTVONRWKLRAINXW443FOF2WC5BAOVWHI4TJMNSX
|
||||
GIDTMNSWYZLSNFZXC5LFEB2XIIDSNBXW4Y3VOMQHM33MOV2HAYLUFQQGQZLOMRZGK4TJOQQGG33O
|
||||
OZQWY3DJOMQG2YLTONQS4ICUN5ZHI33SEBUGS3LFNZQWK33TEBXHK3DMMFWSA3LBNRSXG5LBMRQS
|
||||
A43VONYGK3TENFZXGZJAMNXW443FMN2GK5DVOIQHA33UMVXHI2JAOZXWY5LUOBQXILRAJFXCA43V
|
||||
ONRWS4DJOQQHG2LUEBWWC6DJNV2XGOZANRQWG2LONFQSAZLVEBXHK3DMMEQHA33SORQS4ICUMVWX
|
||||
A5LTEBRW63LNN5SG6IDMNFRGK4TPEBQXKY3UN5ZCAY3SMFZSAYTJMJSW4ZDVNUQHG2LUFYQE2ZLU
|
||||
OVZSAYLQORSW45BANVQXK4TJOMQG4YLTMNSXI5LSEB3GK3TFNZQXI2LTEBSGSYLNEBWWK5DVOMXC
|
||||
ASLOMNSXA5DPOMQG22JAOVWHI4TJMNSXGIDDNRQXG4ZANVXXEYTJEBSG63TFMMXCAT3SMNUSA4DV
|
||||
NR3GS3TBOIQHE2LTOVZSAYLDMN2W243BNYQGMYLVMNUWE5LTEBWGS5DPOJQSAZDVNFZSAY3PNVWW
|
||||
6ZDPEBRW63LNN5SG6LQKBJAWY2LROVQW2IDFORUWC3JANBQWE2LUMFZXGZJAMZQW2ZLTEBWWC43T
|
||||
MEWCA2DFNZSHEZLSNF2CA4DVOJ2XGIDJNZ2GK4TEOVWSA3TVNRWGC3JOEBBXK4TBMJUXI5LSEBYG
|
||||
YYLDMVZGC5BAORXXE5DPOIQHA3DBMNSXEYLUEBXGKYZAMFSGS4DJONRWS3THEBUGCYTJORQXG43F
|
||||
FYQE2YLTONQSA3DJORXXEYJANFXHIZLSMR2W2IDQOJSXI2LVNUQGM5LTMNSSA3TBONRWK5DVOIQH
|
||||
A4TJNVUXGIDGMF2WG2LCOVZSA3LBM5XGCLRAIR2WSIDDOVZGCYTJOR2XEIDNN5WGK43UNFSSA2TV
|
||||
ON2G6IDVNRWGC3LDN5ZHAZLSEBWGK3ZANVXWYZLTORUWKIDNMF2XE2LTFYQES3TDMVYHI33TEBWW
|
||||
C3DFON2WCZDBEB2GCY3JORUSA3DJORXXEYJANRUWEZLSN4QG2YLFMNSW4YLTEBXGK4LVMUQG4YLN
|
||||
EBWGC33SMVSXILRAKRZGS43UNFYXKZJAOZUXMYLNOVZSAZLHMV2CA3LBMVRWK3TBOM5SA4DPOJ2G
|
||||
CIDGMFWWK4ZAMN2XEYLFEBWW63DMNFZSAY3SMFZS4ICJOBZXK3JAOZ2WY4DVORQXIZJAOVWGYYLN
|
||||
MNXXE4DFOIQGK5BANZQXGY3FOR2XEIDOMFZWGZLUOVZCA5TFNQQG64TDNEXAUCSDOVZGCZJAMV2C
|
||||
A33ENFXTWIDVOJXGCIDFOJXXGIDQOVWHM2LOMFZCA3LBNRSXG5LBMRQSAZLHMV2CAZDJM5XGS43T
|
||||
NFWS4ICDOVZHG5LTEBTGK4TNMVXHI5LNEBWGKY3UOVZSA4DIMFZWK3DMOVZSA5DFNVYG64RAOBXX
|
||||
G5LFOJSSA5TVNRYHK5DBORSSA4LVMFWS4ICNMF2XE2LTEBRW63LNN5SG6IDDN5WW233EN4QG2YLV
|
||||
OJUXGIDJNZRWK4DUN5ZSA3LFOR2XGIDWMFZGS5LTEB2XE3TBEBZWCZ3JOR2GS4ZOEBGGSZ3VNRQS
|
||||
AZDVNFZSAZLSMF2CA2LQON2W2IDQOJSXI2LVNUQGK5JAMZQWG2LMNFZWS4ZOEBCHK2LTEBWG6YTP
|
||||
OJ2GS4ZAOBZG62LOEBTGCY3JNRUXG2JAMFSCA43VONRWS4DJOQQHMZLTORUWE5LMOVWSA2DBMMQH
|
||||
I33SORXXEIDJNZ2GK4TEOVWS4ICVNR2HE2LDMVZSA5DJNZRWSZDVNZ2CA3LBPBUW25LTEBQXA5DF
|
||||
NZ2CA4DIMFZWK3DMOVZSA2LOEB2WY3DBNVRW64TQMVZCA3TJONWCA5TBOJUXK4ZAMRUXGLRAKZSW
|
||||
4ZLOMF2GS4ZAMVXGS3JAOBXXIZLOORUSA4DPORSW45DJEBZW6ZDBNRSXGIDNMFZXGYJANFSCAZLM
|
||||
MVWWK3TUOVWS4CQ=
|
Loading…
Add table
Reference in a new issue