Created
April 24, 2023 12:11
-
-
Save unicomp21/3e67673dbd9c86f0b89ac2f1c63b2bb7 to your computer and use it in GitHub Desktop.
code and tests written by gpt-4, aka "gpt protocol buffers"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {HexLengthDelimitedInt} from "./hexLengthDelimitedInt.impl"; | |
function processSerializedString(input: string, offset: number): { length: number, value: string, nextOffset: number } { | |
const lengthResult = HexLengthDelimitedInt.deserialize(input, offset); | |
offset = lengthResult.nextOffset; | |
const value = input.slice(offset, offset + lengthResult.number); | |
offset += lengthResult.number; | |
return {length: lengthResult.number, value, nextOffset: offset}; | |
} | |
export function serializeTagValue(tag: string, value: string): string { | |
const serializedTag = HexLengthDelimitedInt.serialize(tag.length) + tag; | |
const serializedValue = HexLengthDelimitedInt.serialize(value.length) + value; | |
return serializedTag + serializedValue + '\n'; | |
} | |
export function deserializeTagValue(input: string, offset: number): { tag: string, value: string, nextOffset: number } { | |
const tagResult = processSerializedString(input, offset); | |
offset = tagResult.nextOffset; | |
const valueResult = processSerializedString(input, offset); | |
offset = valueResult.nextOffset; | |
return {tag: tagResult.value, value: valueResult.value, nextOffset: offset}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function containsOnlyDecimalDigits(input: string): boolean { | |
for (const char of input) { | |
if (char < '0' || char > '9') { | |
return false; | |
} | |
} | |
return true; | |
} | |
export interface DeserializationResult { | |
number: number; | |
nextOffset: number; | |
} | |
export class HexLengthDelimitedInt { | |
static deserialize(input: string, offset = 0): DeserializationResult { | |
if (input[offset] !== '(') { | |
throw new Error("Invalid input format"); | |
} | |
let nextOffset = -1; | |
for (let i = offset + 1; i < input.length; i++) { | |
if (input[i] === ')') { | |
nextOffset = i; | |
break; | |
} | |
} | |
if (nextOffset === -1) { | |
throw new Error("Invalid input format"); | |
} | |
const length = parseInt(input.slice(offset + 1, offset + 2), 16); | |
if (isNaN(length) || length < 0 || length > 15) { | |
throw new Error("Invalid input length"); | |
} | |
const dec = input.slice(offset + 2, nextOffset); | |
if (!containsOnlyDecimalDigits(dec)) { | |
throw new Error("Invalid decimal digits in length-delimited integer"); | |
} | |
const number = parseInt(dec, 10); | |
if (!Number.isInteger(number)) { | |
throw new Error("Deserialized number is not an integer"); | |
} | |
return {number, nextOffset: nextOffset + 1}; | |
} | |
static serialize(number: number): string { | |
if (!Number.isInteger(number)) { | |
throw new Error("Input number is not an integer"); | |
} | |
const dec = number.toString(10); | |
const length = dec.length; | |
if (length > 15) { | |
throw new Error("Number too large to serialize"); | |
} | |
return '(' + length.toString(16) + dec + ')'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {HexLengthDelimitedInt} from "./hexLengthDelimitedInt.impl"; | |
describe('HexLengthDelimitedInt', () => { | |
test('should serialize and deserialize an integer correctly', () => { | |
const number = 12345; | |
const serialized = HexLengthDelimitedInt.serialize(number); | |
expect(serialized).toBe('(512345)'); | |
const {number: deserialized, nextOffset} = HexLengthDelimitedInt.deserialize(serialized); | |
expect(deserialized).toBe(number); | |
expect(nextOffset).toBe(8); | |
}); | |
test('should throw an error when serializing a non-integer', () => { | |
const number = 123.45; | |
expect(() => HexLengthDelimitedInt.serialize(number)).toThrow('Input number is not an integer'); | |
}); | |
test('should throw an error when deserializing a non-integer', () => { | |
const input = '(51234.5)'; | |
expect(() => HexLengthDelimitedInt.deserialize(input)).toThrow('Invalid decimal digits in length-delimited integer'); | |
}); | |
test('should throw an error when deserializing an invalid input format', () => { | |
const input1 = '512345)'; | |
expect(() => HexLengthDelimitedInt.deserialize(input1)).toThrow('Invalid input format'); | |
const input2 = '(512345'; | |
expect(() => HexLengthDelimitedInt.deserialize(input2)).toThrow('Invalid input format'); | |
}); | |
test('should throw an error when deserializing an input with invalid length', () => { | |
const input = '(g123456123)'; | |
expect(() => HexLengthDelimitedInt.deserialize(input)).toThrow('Invalid input length'); | |
}); | |
test('should throw an error when serializing a number too large to serialize', () => { | |
const number = 0xFFFFFFFFFFFFFFFF; | |
expect(() => HexLengthDelimitedInt.serialize(number)).toThrow('Number too large to serialize'); | |
}); | |
test('should deserialize multiple numbers from the same input string', () => { | |
const input = "(512345)(6123456)"; | |
const {number: deserialized1, nextOffset} = HexLengthDelimitedInt.deserialize(input); | |
expect(deserialized1).toBe(12345); | |
const {number: deserialized2} = HexLengthDelimitedInt.deserialize(input, nextOffset); | |
expect(deserialized2).toBe(123456); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {deserializeTagValue, serializeTagValue} from "./common.impl"; | |
import {HexLengthDelimitedInt} from "./hexLengthDelimitedInt.impl"; | |
export class TagValueArray { | |
public static serialize(array: [string, string][]): string { | |
let result = ''; | |
result += HexLengthDelimitedInt.serialize(array.length) + '\n'; | |
for (let i = 0; i < array.length; i++) { | |
const [tag, value] = array[i]; | |
result += serializeTagValue(tag, value); | |
} | |
return result; | |
} | |
static serializeNoLength(tagValueArray: Array<[string, string]>): string { | |
let message = ''; | |
for (const [tag, value] of tagValueArray) { | |
message += serializeTagValue(tag, value); | |
} | |
return message; | |
} | |
public static serializeLength(array: [string, string][]): number { | |
let length = 0; | |
length += HexLengthDelimitedInt.serialize(array.length).length + 1; // Add 1 for the newline character | |
for (let i = 0; i < array.length; i++) { | |
const [tag, value] = array[i]; | |
length += serializeTagValue(tag, value).length; | |
} | |
return length; | |
} | |
private static processTagValuePairs(input: string, count: number, offset: number): Array<[string, string]> { | |
const tagValueArray: Array<[string, string]> = []; | |
for (let i = 0; i < count; i++) { | |
const { tag, value, nextOffset } = deserializeTagValue(input, offset); | |
offset = nextOffset; | |
tagValueArray.push([tag, value]); | |
if (i < count - 1 && input[offset] !== '\n') { | |
throw new Error("Invalid input format: missing newline character after tag-value pair"); | |
} | |
offset++; | |
} | |
return tagValueArray; | |
} | |
static deserialize(input: string): { array: Array<[string, string]>, nextOffset: number } { | |
const countResult = HexLengthDelimitedInt.deserialize(input); | |
const count = countResult.number; | |
let offset = countResult.nextOffset; | |
if (input[offset] !== '\n') { | |
throw new Error("Invalid input format: missing newline character after count"); | |
} | |
offset++; | |
const array = TagValueArray.processTagValuePairs(input, count, offset); | |
return { array, nextOffset: offset }; | |
} | |
static deserializeNoLength(input: string, count: number): Array<[string, string]> { | |
return TagValueArray.processTagValuePairs(input, count, 0); | |
} | |
static isEmpty(tagValueArray: Array<[string, string]>): boolean { | |
return tagValueArray.length === 0; | |
} | |
static hasTag(tagValueArray: Array<[string, string]>, tag: string): boolean { | |
return tagValueArray.some(([t, _]) => t === tag); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {TagValueArray} from "./tagValueArray.impl"; | |
import {HexLengthDelimitedInt} from "./hexLengthDelimitedInt.impl"; | |
describe('TagValueArray', () => { | |
const testCases = [ | |
{ | |
name: 'Empty array', | |
input: [] as [string, string][], | |
}, | |
{ | |
name: 'Single tag-value pair', | |
input: [['tag1', 'value1']] as [string, string][], | |
}, | |
{ | |
name: 'Multiple tag-value pairs', | |
input: [ | |
['tag1', 'value1'], | |
['tag2', 'value2'], | |
['tag3', 'value3'], | |
] as [string, string][], | |
}, | |
{ | |
name: 'Long tag and value', | |
input: [['tagWithLongName', 'valueWithLongContent']] as [string, string][], | |
}, | |
]; | |
testCases.forEach(testCase => { | |
test(`Serialize and deserialize: ${testCase.name}`, () => { | |
const serialized = TagValueArray.serialize(testCase.input); | |
const deserialized = TagValueArray.deserialize(serialized); | |
expect(deserialized.array).toEqual(testCase.input); | |
}); | |
}); | |
test('Serialize and deserialize: Empty tag or value', () => { | |
const input: [string, string][] = [['tag1', ''], ['', 'value2']]; | |
const serialized = TagValueArray.serialize(input); | |
const deserialized = TagValueArray.deserialize(serialized); | |
expect(deserialized.array).toEqual(input); | |
}); | |
test('Serialize and deserialize: Tag or value containing newline characters', () => { | |
const input: [string, string][] = [['tag\n1', 'value\n1'], ['tag2', 'value\n2']]; | |
const serialized = TagValueArray.serialize(input); | |
const deserialized = TagValueArray.deserialize(serialized); | |
expect(deserialized.array).toEqual(input); | |
}); | |
test('Serialize and deserialize: Tag or value containing special characters', () => { | |
const input: [string, string][] = [['täg1', 'vàlué1'], ['t🚀g2', 'v🌍luè2']]; | |
const serialized = TagValueArray.serialize(input); | |
const deserialized = TagValueArray.deserialize(serialized); | |
expect(deserialized.array).toEqual(input); | |
}); | |
test('SerializeLength', () => { | |
const input: [string, string][] = [['tag1', 'value1'], ['tag2', 'value2'], ['tag3', 'value3']]; | |
const length = TagValueArray.serializeLength(input); | |
const serialized = TagValueArray.serialize(input); | |
expect(length).toEqual(serialized.length); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {TagValueArray} from "./tagValueArray.impl"; | |
export class TagValueMessage { | |
private tagValueMap: Map<string, string>; | |
constructor() { | |
this.tagValueMap = new Map<string, string>(); | |
} | |
set(tag: string, value: string): void { | |
this.tagValueMap.set(tag, value); | |
} | |
get(tag: string): string | undefined { | |
return this.tagValueMap.get(tag); | |
} | |
has(tag: string): boolean { | |
return this.tagValueMap.has(tag); | |
} | |
delete(tag: string): boolean { | |
return this.tagValueMap.delete(tag); | |
} | |
clear(): void { | |
this.tagValueMap.clear(); | |
} | |
serialize(): string { | |
const tagValueArray: Array<[string, string]> = []; | |
for (const [tag, value] of this.tagValueMap.entries()) { | |
tagValueArray.push([tag, value]); | |
} | |
return TagValueArray.serialize(tagValueArray); | |
} | |
deserialize(input: string): number { | |
const { array, nextOffset } = TagValueArray.deserialize(input); | |
this.tagValueMap.clear(); | |
for (const [tag, value] of array) { | |
this.set(tag, value); | |
} | |
return nextOffset; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {TagValueMessage} from "./tagValueMessage.impl"; | |
describe('TagValueMessage', () => { | |
let message: TagValueMessage; | |
beforeEach(() => { | |
message = new TagValueMessage(); | |
}); | |
test('set, get, has, and delete methods', () => { | |
expect(message.has('tag1')).toBe(false); | |
expect(message.get('tag1')).toBeUndefined(); | |
message.set('tag1', 'value1'); | |
expect(message.has('tag1')).toBe(true); | |
expect(message.get('tag1')).toBe('value1'); | |
message.set('tag2', 'value2'); | |
expect(message.has('tag2')).toBe(true); | |
expect(message.get('tag2')).toBe('value2'); | |
expect(message.delete('tag1')).toBe(true); | |
expect(message.has('tag1')).toBe(false); | |
expect(message.get('tag1')).toBeUndefined(); | |
expect(message.delete('nonexistent')).toBe(false); | |
}); | |
test('clear method', () => { | |
message.set('tag1', 'value1'); | |
message.set('tag2', 'value2'); | |
message.clear(); | |
expect(message.has('tag1')).toBe(false); | |
expect(message.get('tag1')).toBeUndefined(); | |
expect(message.has('tag2')).toBe(false); | |
expect(message.get('tag2')).toBeUndefined(); | |
}); | |
test('serialize and deserialize methods', () => { | |
message.set('tag1', 'value1'); | |
message.set('tag2', 'value2'); | |
const serialized = message.serialize(); | |
const deserializedMessage = new TagValueMessage(); | |
deserializedMessage.deserialize(serialized); | |
expect(deserializedMessage.has('tag1')).toBe(true); | |
expect(deserializedMessage.get('tag1')).toBe('value1'); | |
expect(deserializedMessage.has('tag2')).toBe(true); | |
expect(deserializedMessage.get('tag2')).toBe('value2'); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment