///|
/// Bech32 checksum variant.
pub(all) enum Variant {
Bech32
Bech32m
} derive(Eq, Debug)
///|
/// A decoded Bech32 or Bech32m string. `data` contains 5-bit values with the
/// six checksum words removed.
pub(all) struct Decoded {
hrp : String
data : Array[Int]
variant : Variant
} derive(Eq, Debug)
///|
/// A decoded BIP-0173/BIP-0350 SegWit address.
pub(all) struct SegwitAddress {
hrp : String
version : Int
program : Array[Int]
variant : Variant
} derive(Eq, Debug)
///|
/// Letter-case classification for Bech32 input strings.
pub(all) enum CaseStyle {
CaseNoLetters
CaseLower
CaseUpper
CaseMixed
} derive(Eq, Debug)
///|
/// Common SegWit network names inferred from the human-readable part.
pub(all) enum SegwitNetwork {
NetworkBitcoinMainnet
NetworkBitcoinTestnet
NetworkBitcoinRegtest
NetworkUnknown(String)
} derive(Eq, Debug)
///|
/// Common witness program categories useful for wallet and indexer diagnostics.
pub(all) enum WitnessProgramKind {
WitnessProgramP2WPKH
WitnessProgramP2WSH
WitnessProgramTaproot
WitnessProgramOther
} derive(Eq, Debug)
///|
/// Mechanical profile of an input string. This is available even for malformed
/// inputs and is useful for command-line diagnostics.
pub(all) struct InputProfile {
input : String
normalized : String
total_length : Int
separator_index : Int
hrp_length : Int
data_part_length : Int
payload_length : Int
checksum_length : Int
case_style : CaseStyle
has_separator : Bool
has_checksum : Bool
} derive(Eq, Debug)
///|
/// Detailed information for a valid Bech32 or Bech32m string.
pub(all) struct Bech32Info {
input : String
normalized : String
hrp : String
data : Array[Int]
checksum : Array[Int]
variant : Variant
total_length : Int
separator_index : Int
data_part_length : Int
data_length : Int
checksum_length : Int
case_style : CaseStyle
segwit_valid : Bool
} derive(Eq, Debug)
///|
/// Detailed information for a valid SegWit address.
pub(all) struct SegwitInfo {
input : String
normalized : String
hrp : String
version : Int
program : Array[Int]
program_length : Int
variant : Variant
network : SegwitNetwork
program_kind : WitnessProgramKind
} derive(Eq, Debug)
///|
/// Stable validation report for batch tools. String fields are intentionally
/// presentation-ready so callers can print reports without matching every enum.
pub(all) struct ValidationReport {
input : String
normalized : String
valid : Bool
segwit_valid : Bool
variant : String
hrp : String
error_code : String
error_message : String
total_length : Int
separator_index : Int
data_part_length : Int
} derive(Eq, Debug)
///|
/// Structured errors returned by the parser and validators.
pub(all) enum Bech32Error {
EmptyInput
TooLong(Int)
MissingSeparator
EmptyHrp
EmptyData
MixedCase
InvalidHrpChar(Char, Int)
InvalidDataChar(Char, Int)
InvalidDataValue(Int, Int)
InvalidChecksum
InvalidBitGroup(Int, Int)
InvalidPadding
InvalidWitnessVersion(Int)
InvalidWitnessProgramLength(Int, Int)
InvalidWitnessEncoding(Variant, Variant)
UnexpectedHrp(String, String)
UnexpectedNetwork(SegwitNetwork, SegwitNetwork)
} derive(Eq, Debug)
///|
const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
///|
const BECH32_CONST = 1
///|
const BECH32M_CONST = 0x2bc830a3
///|
const MAX_BECH32_LEN = 90
///|
/// Encodes 5-bit data words with a Bech32 checksum.
pub fn encode(hrp : String, data : Array[Int]) -> Result[String, Bech32Error] {
encode_with_variant(hrp, data, Bech32)
}
///|
/// Encodes 5-bit data words with a Bech32m checksum.
pub fn encode_m(hrp : String, data : Array[Int]) -> Result[String, Bech32Error] {
encode_with_variant(hrp, data, Bech32m)
}
///|
/// Encodes 5-bit data words with the selected checksum variant.
pub fn encode_with_variant(
hrp : String,
data : Array[Int],
variant : Variant,
) -> Result[String, Bech32Error] {
match validate_hrp(hrp) {
Err(err) => return Err(err)
Ok(_) => ()
}
match validate_data(data) {
Err(err) => return Err(err)
Ok(_) => ()
}
let lower_hrp = hrp.to_lower()
let checksum = create_checksum(lower_hrp, data, variant)
let out = StringBuilder(size_hint=lower_hrp.length() + 1 + data.length() + 6)
out.write_string(lower_hrp)
out.write_char('1')
for value in data {
out.write_char(char_at(CHARSET, value))
}
for value in checksum {
out.write_char(char_at(CHARSET, value))
}
let encoded = out.to_string()
if encoded.length() > MAX_BECH32_LEN {
Err(TooLong(encoded.length()))
} else {
Ok(encoded)
}
}
///|
/// Decodes a Bech32 or Bech32m string and verifies its checksum.
pub fn decode(input : String) -> Result[Decoded, Bech32Error] {
if input.is_empty() {
return Err(EmptyInput)
}
if input.length() > MAX_BECH32_LEN {
return Err(TooLong(input.length()))
}
if has_mixed_case(input) {
return Err(MixedCase)
}
let text = input.to_lower()
let sep = last_separator(text)
if sep < 0 {
return Err(MissingSeparator)
}
if sep == 0 {
return Err(EmptyHrp)
}
if sep + 7 > text.length() {
return Err(EmptyData)
}
let hrp = text[:sep].to_owned()
match validate_hrp(hrp) {
Err(err) => return Err(err)
Ok(_) => ()
}
let data = Array::new(capacity=text.length() - sep - 1)
for i in (sep + 1).. data.push(value)
None => return Err(InvalidDataChar(ch, i))
}
}
let pm = polymod(expand_hrp(hrp) + data)
let variant = if pm == BECH32_CONST {
Bech32
} else if pm == BECH32M_CONST {
Bech32m
} else {
return Err(InvalidChecksum)
}
Ok({ hrp, data: data[:data.length() - 6].to_owned(), variant })
}
///|
/// Converts an array of unsigned integer groups from one bit width to another.
/// Use `pad=false` when decoding canonical encodings back to bytes.
pub fn convert_bits(
data : Array[Int],
from_bits : Int,
to_bits : Int,
pad : Bool,
) -> Result[Array[Int], Bech32Error] {
if from_bits <= 0 || to_bits <= 0 || from_bits > 30 || to_bits > 30 {
return Err(InvalidBitGroup(from_bits, to_bits))
}
let maxv = (1 << to_bits) - 1
let max_acc = (1 << (from_bits + to_bits - 1)) - 1
let ret = Array::new()
let mut acc = 0
let mut bits = 0
for value in data {
if value < 0 || value >> from_bits != 0 {
return Err(InvalidDataValue(value, ret.length()))
}
acc = ((acc << from_bits) | value) & max_acc
bits += from_bits
while bits >= to_bits {
bits -= to_bits
ret.push((acc >> bits) & maxv)
}
}
if pad {
if bits > 0 {
ret.push((acc << (to_bits - bits)) & maxv)
}
} else if bits >= from_bits || ((acc << (to_bits - bits)) & maxv) != 0 {
return Err(InvalidPadding)
}
Ok(ret)
}
///|
/// Encodes byte values (`0..255`) as Bech32 data.
pub fn encode_bytes(
hrp : String,
bytes : Array[Int],
variant : Variant,
) -> Result[String, Bech32Error] {
match validate_octets(bytes) {
Err(err) => Err(err)
Ok(_) =>
match convert_bits(bytes, 8, 5, true) {
Ok(words) => encode_with_variant(hrp, words, variant)
Err(err) => Err(err)
}
}
}
///|
/// Decodes Bech32 data back to byte values (`0..255`).
pub fn decode_bytes(
input : String,
) -> Result[(String, Array[Int], Variant), Bech32Error] {
match decode(input) {
Err(err) => Err(err)
Ok(decoded) =>
match convert_bits(decoded.data, 5, 8, false) {
Ok(bytes) => Ok((decoded.hrp, bytes, decoded.variant))
Err(err) => Err(err)
}
}
}
///|
/// Encodes a SegWit address. Version 0 uses Bech32; versions 1 through 16 use
/// Bech32m, as required by BIP-0350.
pub fn encode_segwit(
hrp : String,
version : Int,
program : Array[Int],
) -> Result[String, Bech32Error] {
match validate_witness_program(version, program) {
Err(err) => return Err(err)
Ok(_) => ()
}
let variant = witness_variant(version)
match convert_bits(program, 8, 5, true) {
Err(err) => Err(err)
Ok(words) => {
let data = Array::new(capacity=words.length() + 1)
data.push(version)
data.append(words)
encode_with_variant(hrp, data, variant)
}
}
}
///|
/// Decodes and validates a SegWit address.
pub fn decode_segwit(input : String) -> Result[SegwitAddress, Bech32Error] {
match decode(input) {
Err(err) => Err(err)
Ok(decoded) => {
if decoded.data.is_empty() {
return Err(EmptyData)
}
let version = decoded.data[0]
if version < 0 || version > 16 {
return Err(InvalidWitnessVersion(version))
}
let payload = decoded.data[1:]
match convert_bits(payload.to_owned(), 5, 8, false) {
Err(err) => Err(err)
Ok(program) => {
match validate_witness_program(version, program) {
Err(err) => return Err(err)
Ok(_) => ()
}
let expected = witness_variant(version)
if decoded.variant != expected {
return Err(InvalidWitnessEncoding(expected, decoded.variant))
}
Ok({ hrp: decoded.hrp, version, program, variant: decoded.variant })
}
}
}
}
}
///|
/// Returns true when `input` is a valid Bech32 or Bech32m string.
pub fn is_valid(input : String) -> Bool {
decode(input) is Ok(_)
}
///|
/// Returns true when `input` is a valid SegWit address.
pub fn is_valid_segwit(input : String) -> Bool {
decode_segwit(input) is Ok(_)
}
///|
/// Returns a stable lowercase display name for a checksum variant.
pub fn variant_name(variant : Variant) -> String {
match variant {
Bech32 => "bech32"
Bech32m => "bech32m"
}
}
///|
/// Returns a stable lowercase display name for input letter-case style.
pub fn case_style_name(style : CaseStyle) -> String {
match style {
CaseNoLetters => "no_letters"
CaseLower => "lower"
CaseUpper => "upper"
CaseMixed => "mixed"
}
}
///|
/// Returns a stable display name for a SegWit network classification.
pub fn network_name(network : SegwitNetwork) -> String {
match network {
NetworkBitcoinMainnet => "bitcoin_mainnet"
NetworkBitcoinTestnet => "bitcoin_testnet"
NetworkBitcoinRegtest => "bitcoin_regtest"
NetworkUnknown(_) => "unknown"
}
}
///|
/// Returns a stable display name for a witness program category.
pub fn witness_program_kind_name(kind : WitnessProgramKind) -> String {
match kind {
WitnessProgramP2WPKH => "p2wpkh"
WitnessProgramP2WSH => "p2wsh"
WitnessProgramTaproot => "taproot"
WitnessProgramOther => "other"
}
}
///|
/// Classifies whether an input is lowercase, uppercase, mixed-case, or has no
/// ASCII letters. Bech32 accepts only all-lowercase or all-uppercase strings.
pub fn classify_case(input : String) -> CaseStyle {
let mut has_lower = false
let mut has_upper = false
for ch in input {
if ch.is_ascii_lowercase() {
has_lower = true
} else if ch.is_ascii_uppercase() {
has_upper = true
}
}
if has_lower && has_upper {
CaseMixed
} else if has_lower {
CaseLower
} else if has_upper {
CaseUpper
} else {
CaseNoLetters
}
}
///|
/// Builds a structural profile without validating checksum or character set.
pub fn profile(input : String) -> InputProfile {
let normalized = input.to_lower()
let sep = last_separator(normalized)
let has_sep = sep >= 0
let data_part_length = if has_sep { input.length() - sep - 1 } else { 0 }
let payload_length = if data_part_length >= 6 {
data_part_length - 6
} else {
0
}
let checksum_length = if data_part_length >= 6 { 6 } else { data_part_length }
{
input,
normalized,
total_length: input.length(),
separator_index: sep,
hrp_length: if has_sep {
sep
} else {
0
},
data_part_length,
payload_length,
checksum_length,
case_style: classify_case(input),
has_separator: has_sep,
has_checksum: data_part_length >= 6,
}
}
///|
/// Returns a lowercase canonical representation after checksum validation.
pub fn normalize(input : String) -> Result[String, Bech32Error] {
match decode(input) {
Err(err) => Err(err)
Ok(_) => Ok(input.to_lower())
}
}
///|
/// Returns true when an input is valid and already in canonical lowercase form.
pub fn is_canonical(input : String) -> Bool {
match normalize(input) {
Err(_) => false
Ok(canonical) => canonical == input
}
}
///|
/// Returns true only for valid Bech32 strings.
pub fn is_bech32(input : String) -> Bool {
match decode(input) {
Err(_) => false
Ok(decoded) => decoded.variant == Bech32
}
}
///|
/// Returns true only for valid Bech32m strings.
pub fn is_bech32m(input : String) -> Bool {
match decode(input) {
Err(_) => false
Ok(decoded) => decoded.variant == Bech32m
}
}
///|
/// Extracts the HRP after full checksum validation.
pub fn hrp_of(input : String) -> Result[String, Bech32Error] {
match decode(input) {
Err(err) => Err(err)
Ok(decoded) => Ok(decoded.hrp)
}
}
///|
/// Extracts payload data words after full checksum validation.
pub fn data_words_of(input : String) -> Result[Array[Int], Bech32Error] {
match decode(input) {
Err(err) => Err(err)
Ok(decoded) => Ok(decoded.data)
}
}
///|
/// Extracts the six checksum words after full checksum validation.
pub fn checksum_words_of(input : String) -> Result[Array[Int], Bech32Error] {
match decode(input) {
Err(err) => Err(err)
Ok(_) => Ok(checksum_words_from_normalized(input.to_lower()))
}
}
///|
/// Decodes an input and additionally checks the expected HRP.
pub fn decode_with_hrp(
input : String,
expected_hrp : String,
) -> Result[Decoded, Bech32Error] {
match validate_hrp(expected_hrp) {
Err(err) => return Err(err)
Ok(_) => ()
}
let expected = expected_hrp.to_lower()
match decode(input) {
Err(err) => Err(err)
Ok(decoded) =>
if decoded.hrp == expected {
Ok(decoded)
} else {
Err(UnexpectedHrp(expected, decoded.hrp))
}
}
}
///|
/// Inspects a valid Bech32 or Bech32m string and returns printable metadata.
pub fn inspect(input : String) -> Result[Bech32Info, Bech32Error] {
match decode(input) {
Err(err) => Err(err)
Ok(decoded) => {
let normalized = input.to_lower()
let sep = last_separator(normalized)
let data_part_length = normalized.length() - sep - 1
Ok({
input,
normalized,
hrp: decoded.hrp,
data: decoded.data,
checksum: checksum_words_from_normalized(normalized),
variant: decoded.variant,
total_length: input.length(),
separator_index: sep,
data_part_length,
data_length: data_part_length - 6,
checksum_length: 6,
case_style: classify_case(input),
segwit_valid: decode_segwit(input) is Ok(_),
})
}
}
}
///|
/// Produces one validation report that is convenient for CLI and UI callers.
pub fn validation_report(input : String) -> ValidationReport {
let p = profile(input)
match decode(input) {
Err(err) =>
{
input,
normalized: p.normalized,
valid: false,
segwit_valid: false,
variant: "",
hrp: "",
error_code: error_code(err),
error_message: error_message(err),
total_length: p.total_length,
separator_index: p.separator_index,
data_part_length: p.data_part_length,
}
Ok(decoded) =>
{
input,
normalized: p.normalized,
valid: true,
segwit_valid: decode_segwit(input) is Ok(_),
variant: variant_name(decoded.variant),
hrp: decoded.hrp,
error_code: "",
error_message: "",
total_length: p.total_length,
separator_index: p.separator_index,
data_part_length: p.data_part_length,
}
}
}
///|
/// Validates many inputs while preserving input order.
pub fn validate_many(inputs : Array[String]) -> Array[ValidationReport] {
let reports = Array::new(capacity=inputs.length())
for input in inputs {
reports.push(validation_report(input))
}
reports
}
///|
/// Counts valid Bech32 or Bech32m strings in a batch.
pub fn valid_count(inputs : Array[String]) -> Int {
let mut count = 0
for input in inputs {
if is_valid(input) {
count += 1
}
}
count
}
///|
/// Counts invalid Bech32 or Bech32m strings in a batch.
pub fn invalid_count(inputs : Array[String]) -> Int {
inputs.length() - valid_count(inputs)
}
///|
/// Returns true when every input in a batch is valid.
pub fn all_valid(inputs : Array[String]) -> Bool {
valid_count(inputs) == inputs.length()
}
///|
/// Counts valid SegWit addresses in a batch.
pub fn segwit_valid_count(inputs : Array[String]) -> Int {
let mut count = 0
for input in inputs {
if is_valid_segwit(input) {
count += 1
}
}
count
}
///|
/// Infers the common SegWit network from an HRP.
pub fn segwit_network(hrp : String) -> SegwitNetwork {
let lower = hrp.to_lower()
if lower == "bc" {
NetworkBitcoinMainnet
} else if lower == "tb" {
NetworkBitcoinTestnet
} else if lower == "bcrt" {
NetworkBitcoinRegtest
} else {
NetworkUnknown(lower)
}
}
///|
/// Returns true for standard Bitcoin SegWit HRPs: bc, tb, and bcrt.
pub fn is_standard_segwit_hrp(hrp : String) -> Bool {
match segwit_network(hrp) {
NetworkBitcoinMainnet => true
NetworkBitcoinTestnet => true
NetworkBitcoinRegtest => true
NetworkUnknown(_) => false
}
}
///|
/// Infers the SegWit network after full SegWit address validation.
pub fn segwit_network_of(input : String) -> Result[SegwitNetwork, Bech32Error] {
match decode_segwit(input) {
Err(err) => Err(err)
Ok(address) => Ok(segwit_network(address.hrp))
}
}
///|
/// Classifies a witness program by version and program length.
pub fn witness_program_kind(
version : Int,
program_length : Int,
) -> WitnessProgramKind {
if version == 0 && program_length == 20 {
WitnessProgramP2WPKH
} else if version == 0 && program_length == 32 {
WitnessProgramP2WSH
} else if version == 1 && program_length == 32 {
WitnessProgramTaproot
} else {
WitnessProgramOther
}
}
///|
/// Inspects a valid SegWit address and returns network and witness metadata.
pub fn inspect_segwit(input : String) -> Result[SegwitInfo, Bech32Error] {
match decode_segwit(input) {
Err(err) => Err(err)
Ok(address) => {
let program_length = address.program.length()
Ok({
input,
normalized: input.to_lower(),
hrp: address.hrp,
version: address.version,
program: address.program,
program_length,
variant: address.variant,
network: segwit_network(address.hrp),
program_kind: witness_program_kind(address.version, program_length),
})
}
}
}
///|
/// Decodes a SegWit address and additionally checks the expected network.
pub fn decode_segwit_on_network(
input : String,
expected : SegwitNetwork,
) -> Result[SegwitAddress, Bech32Error] {
match decode_segwit(input) {
Err(err) => Err(err)
Ok(address) => {
let actual = segwit_network(address.hrp)
if actual == expected {
Ok(address)
} else {
Err(UnexpectedNetwork(expected, actual))
}
}
}
}
///|
/// Stable machine-readable code for every structured error.
pub fn error_code(err : Bech32Error) -> String {
match err {
EmptyInput => "empty_input"
TooLong(_) => "too_long"
MissingSeparator => "missing_separator"
EmptyHrp => "empty_hrp"
EmptyData => "empty_data"
MixedCase => "mixed_case"
InvalidHrpChar(_, _) => "invalid_hrp_char"
InvalidDataChar(_, _) => "invalid_data_char"
InvalidDataValue(_, _) => "invalid_data_value"
InvalidChecksum => "invalid_checksum"
InvalidBitGroup(_, _) => "invalid_bit_group"
InvalidPadding => "invalid_padding"
InvalidWitnessVersion(_) => "invalid_witness_version"
InvalidWitnessProgramLength(_, _) => "invalid_witness_program_length"
InvalidWitnessEncoding(_, _) => "invalid_witness_encoding"
UnexpectedHrp(_, _) => "unexpected_hrp"
UnexpectedNetwork(_, _) => "unexpected_network"
}
}
///|
/// Human-readable message for every structured error.
pub fn error_message(err : Bech32Error) -> String {
match err {
EmptyInput => "input is empty"
TooLong(_) => "input exceeds the Bech32 length limit"
MissingSeparator => "separator '1' is missing"
EmptyHrp => "human-readable part is empty"
EmptyData => "data part is too short"
MixedCase => "input mixes lowercase and uppercase letters"
InvalidHrpChar(_, _) => "human-readable part contains an invalid character"
InvalidDataChar(_, _) =>
"data part contains a character outside the Bech32 charset"
InvalidDataValue(_, _) => "data value is outside the allowed range"
InvalidChecksum => "checksum does not match Bech32 or Bech32m"
InvalidBitGroup(_, _) => "bit group width is outside the supported range"
InvalidPadding => "bit conversion padding is not canonical"
InvalidWitnessVersion(_) => "witness version must be between 0 and 16"
InvalidWitnessProgramLength(_, _) =>
"witness program length is invalid for this version"
InvalidWitnessEncoding(_, _) =>
"witness version uses the wrong checksum variant"
UnexpectedHrp(_, _) => "decoded HRP does not match the expected HRP"
UnexpectedNetwork(_, _) =>
"decoded SegWit network does not match the expected network"
}
}
///|
fn validate_hrp(hrp : String) -> Result[Unit, Bech32Error] {
if hrp.is_empty() {
return Err(EmptyHrp)
}
for i in 0.. 126 {
return Err(InvalidHrpChar(ch, i))
}
}
if has_mixed_case(hrp) {
Err(MixedCase)
} else {
Ok(())
}
}
///|
fn validate_data(data : Array[Int]) -> Result[Unit, Bech32Error] {
for i, value in data.iter2() {
if value < 0 || value > 31 {
return Err(InvalidDataValue(value, i))
}
}
Ok(())
}
///|
fn validate_octets(data : Array[Int]) -> Result[Unit, Bech32Error] {
for i, value in data.iter2() {
if value < 0 || value > 255 {
return Err(InvalidDataValue(value, i))
}
}
Ok(())
}
///|
fn validate_witness_program(
version : Int,
program : Array[Int],
) -> Result[Unit, Bech32Error] {
if version < 0 || version > 16 {
return Err(InvalidWitnessVersion(version))
}
match validate_octets(program) {
Err(err) => return Err(err)
Ok(_) => ()
}
let len = program.length()
if len < 2 || len > 40 {
return Err(InvalidWitnessProgramLength(version, len))
}
if version == 0 && !(len == 20 || len == 32) {
return Err(InvalidWitnessProgramLength(version, len))
}
Ok(())
}
///|
fn witness_variant(version : Int) -> Variant {
if version == 0 {
Bech32
} else {
Bech32m
}
}
///|
fn has_mixed_case(input : String) -> Bool {
classify_case(input) == CaseMixed
}
///|
fn last_separator(input : String) -> Int {
let mut sep = -1
for i in 0.. Int? {
for i in 0.. Array[Int] {
let out = Array::new(capacity=hrp.length() * 2 + 1)
for ch in hrp {
out.push(ch.to_int() >> 5)
}
out.push(0)
for ch in hrp {
out.push(ch.to_int() & 31)
}
out
}
///|
fn polymod(values : Array[Int]) -> Int {
let generators = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
let mut chk = 1
for value in values {
let top = chk >> 25
chk = ((chk & 0x1ffffff) << 5) ^ value
for i in 0..<5 {
if ((top >> i) & 1) == 1 {
chk = chk ^ generators[i]
}
}
}
chk
}
///|
fn create_checksum(
hrp : String,
data : Array[Int],
variant : Variant,
) -> Array[Int] {
let values = expand_hrp(hrp) + data
for _ in 0..<6 {
values.push(0)
}
let constant = match variant {
Bech32 => BECH32_CONST
Bech32m => BECH32M_CONST
}
let pm = polymod(values) ^ constant
Array::makei(6, i => (pm >> (5 * (5 - i))) & 31)
}
///|
fn checksum_words_from_normalized(input : String) -> Array[Int] {
let checksum = Array::new(capacity=6)
for i in (input.length() - 6).. Char {
input.get_char(offset).unwrap()
}