Skip to content

Instantly share code, notes, and snippets.

@syminical
Last active July 20, 2020 07:26
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 syminical/27d0a3f13423ad8a1e39526afae8ba94 to your computer and use it in GitHub Desktop.
Save syminical/27d0a3f13423ad8a1e39526afae8ba94 to your computer and use it in GitHub Desktop.
WeirdTextFormat - This class handles encoding and decoding of text using the weird text format.
# Copyright (c) 2020 Alexandru Manaila. All Rights Reserved.
# 2020/3/12
import math
class AWeirdTextFormat:
"""
This class handles encoding and decoding of text
using the weird text format.
"""
# !!! Decode() will strip all null characters !!!
# Supporting them was not requested, and requires nasty work-arounds.
# Some notes about \x00 (the null character):
# 1.) ord(\x00) = 0, binary form is eight 0's in the context of this class.
# 2.) Encode cannot accurately encode trailing null characters in a string.
# a. Encode("\x00\x00\x00") = [0] = Encode("\x00\x00") = ...
# 3.) Decode cannot accurately decode leading null characters in integers.
# a. Decode(Encode('\x00W')) = '\x00\x00\x00W' =
# Decode(Encode('\x00\x00W')) = Decode(Encode('\x00\x00\x00W'))
# These mappings are designed to avoid string reversals while processing
# the input string characters in order.
# Diagram: EncodingMap.png
__kEncodingMap = {
0:3, 1:7, 2:11, 3:15, 4:19, 5:23, 6:27, 7:31,
8:2, 9:6, 10:10, 11:14, 12:18, 13:22, 14:26, 15:30,
16:1, 17:5, 18:9, 19:13, 20:17, 21:21, 22:25, 23:29,
24:0, 25:4, 26:8, 27:12, 28:16, 29:20, 30:24, 31:28
}
# Diagram: DecodingMap.png
__kDecodingMap = {
0:0, 1:8, 2:16, 3:24, 4:1, 5:9, 6:17, 7:25,
8:2, 9:10, 10:18, 11:26, 12:3, 13:11, 14:19, 15:27,
16:4, 17:12, 18:20, 19:28, 20:5, 21:13, 22:21, 23:29,
24:6, 25:14, 26:22, 27:30, 28:7, 29:15, 30:23, 31:31
}
def Encode(inputString):
"""
Encodes a string using kEncodingMap.
input: String
output: List that contains integers, or empty if character bits overflow.
"""
ret = [0] * math.ceil(len(inputString) / 4)
# Each iteration adds a new value to ret.
# A list is filled with binary representations of every four characters.
# That list is then converted to an integer, and collected in ret.
for iRet, iSlice in enumerate(range(0, len(inputString), 4)):
#This solves dealing with strings whose lengths != 4k, k is an int.
# Also, there are usually less 1's than 0's to move around.
encodedBinTray = ['0'] * 32
fourCharChunk = inputString[iSlice:iSlice + 4]
# Each iteration processes a char from the 4-char segment above.
for iByte, char in enumerate(fourCharChunk):
if 255 < ord(char):
# char will not be decoded properly.
return []
# Each iteration processes a bit from the binary representation of
# the char above.
# Need :08b instead of :b to properly pad smaller numbers.
for offset, bit in enumerate(f'{ord(char):08b}'):
if '1' == bit:
encodedBinTray[AWeirdTextFormat.__kEncodingMap[8 * iByte +
offset]] = '1'
ret[iRet] = int(''.join(encodedBinTray), 2)
return ret
def Decode(inputList):
"""
Decodes a list of integers using kDecodingMap.
input: List of integers.
output: String, may be empty if the bits from input integers overflow.
Null characters are not included in the result.
"""
ret = [""] * len(inputList)
# Each iteration adds a chunk of four characters to ret.
# The bits of the binary form of each number in inputList are inserted
# into a list (32bit). The list of bits is decoded, then each byte is
# converted into a unicode character. The characters are collected
# in ret as chunks of four, and returned at the end as a single string.
for iRet, encodedNum in enumerate(inputList):
if 4294967295 < encodedNum:
# encodedNum cannot be decoded.
return ''
decodedBinTray = ['0'] * 32
#Each iteration decodes a bit from the binary form of encodedNum.
#Use :032b to properly fill in the tray for nums with fewer bits.
for iMap, bit in enumerate(f'{encodedNum:032b}'):
if '1' == bit:
decodedBinTray[AWeirdTextFormat.__kDecodingMap[iMap]] = '1'
decodedBinStr = ''.join(decodedBinTray)
# Converts each byte into a character; reads decodedBinStr backwards
# to preserve the correct character order.
fourCharChunks = []
for i in range(24, -1, -8):
char = chr(int(decodedBinStr[i:i + 8], 2))
# Do not include null characters.
if '\x00' != char:
fourCharChunks.append(char)
ret[iRet] = ''.join(fourCharChunks)
return ''.join(ret)
# I/O program
def AWTF(_):
return AWeirdTextFormat.Decode(AWeirdTextFormat.Encode(_))
# Reads input.txt as a string, encrypts it, decrypts that, then prints result.
with open('input.txt', 'r') as inputFile:
print(AWTF(inputFile.read()))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment