Created
October 2, 2024 01:11
-
-
Save sanrai/128cf8091b8659dd883eac5e5d9a86fa to your computer and use it in GitHub Desktop.
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
// URL-safe character set for Base85 encoding (now with 85 characters) | |
const URL_SAFE_CHARS = [ | |
// Digits (10) | |
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | |
// Uppercase letters (26) | |
'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', | |
// Lowercase letters (26, including 'z') | |
'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', | |
// Unreserved punctuation (4) | |
'-', '.', '_', '~', | |
// Sub-delimiters and additional characters (19) | |
'!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '=', ':', '@', | |
'/', '?', '[', ']', '`', '^' // Added '^' to reach 85 characters | |
]; | |
// Mapping from character to its index in URL_SAFE_CHARS | |
const URL_SAFE_CHARS_MAP = {}; | |
for (let i = 0; i < URL_SAFE_CHARS.length; i++) { | |
URL_SAFE_CHARS_MAP[URL_SAFE_CHARS[i]] = i; | |
} | |
// Base85 encoding function using BigInt and URL-safe characters | |
function base85Encode(bytes) { | |
let output = ''; | |
let i = 0; | |
while (i < bytes.length) { | |
let chunkSize = Math.min(4, bytes.length - i); | |
let value = BigInt(0); | |
for (let j = 0; j < chunkSize; j++) { | |
value = (value << BigInt(8)) | BigInt(bytes[i + j]); | |
} | |
// Pad value if chunkSize < 4 | |
if (chunkSize < 4) { | |
value <<= BigInt((4 - chunkSize) * 8); | |
} | |
// Generate the encoded string | |
let encoded = ''; | |
for (let j = 0; j < 5; j++) { | |
let index = Number(value % BigInt(85)); | |
let char = URL_SAFE_CHARS[index]; | |
encoded = char + encoded; | |
value = value / BigInt(85); | |
} | |
if (chunkSize < 4) { | |
// Since 'encoded' is built from right to left, extract from the start | |
encoded = encoded.substring(0, chunkSize + 1); | |
} | |
output += encoded; | |
i += chunkSize; | |
} | |
return output; | |
} | |
// Base85 decoding function using BigInt and URL-safe characters | |
function base85Decode(input) { | |
input = input.replace(/\s+/g, ''); | |
const outputBytes = []; | |
let value = BigInt(0); | |
let count = 0; | |
for (let i = 0; i < input.length; i++) { | |
const c = input[i]; | |
if (!(c in URL_SAFE_CHARS_MAP)) { | |
throw new Error(`Invalid character '${c}' in Base85 encoding.`); | |
} | |
value = value * BigInt(85) + BigInt(URL_SAFE_CHARS_MAP[c]); | |
count++; | |
if (count === 5) { | |
const bytes = []; | |
for (let j = 3; j >= 0; j--) { | |
bytes[j] = Number(value & BigInt(0xFF)); | |
value >>= BigInt(8); | |
} | |
outputBytes.push(...bytes); | |
value = BigInt(0); | |
count = 0; | |
} | |
} | |
// Handle remaining characters | |
if (count > 0) { | |
for (let i = count; i < 5; i++) { | |
value = value * BigInt(85) + BigInt(84); // Padding with the highest value | |
} | |
const bytes = []; | |
for (let j = 3; j >= 0; j--) { | |
bytes[j] = Number(value & BigInt(0xFF)); | |
value >>= BigInt(8); | |
} | |
const bytesToExtract = count - 1; | |
outputBytes.push(...bytes.slice(0, bytesToExtract)); | |
} | |
return new Uint8Array(outputBytes); | |
} | |
// Base64 encoding function (unchanged) | |
function base64Encode(uint8Array) { | |
let binary = ''; | |
const len = uint8Array.length; | |
for (let i = 0; i < len; i++) { | |
binary += String.fromCharCode(uint8Array[i]); | |
} | |
return btoa(binary); | |
} | |
// Base64 decoding function (unchanged) | |
function base64Decode(str) { | |
const binaryString = atob(str); | |
const len = binaryString.length; | |
const uint8Array = new Uint8Array(len); | |
for (let i = 0; i < len; i++) { | |
uint8Array[i] = binaryString.charCodeAt(i); | |
} | |
return uint8Array; | |
} | |
// Compression function using CompressionStream (unchanged) | |
async function compressString(inputStr) { | |
const encoder = new TextEncoder(); | |
const inputBytes = encoder.encode(inputStr); | |
const compressedStream = new Blob([inputBytes]).stream().pipeThrough(new CompressionStream('gzip')); | |
const compressedArrayBuffer = await new Response(compressedStream).arrayBuffer(); | |
const compressedBytes = new Uint8Array(compressedArrayBuffer); | |
return compressedBytes; | |
} | |
// Decompression function using DecompressionStream (unchanged) | |
async function decompressBytes(compressedBytes) { | |
const decompressedStream = new Blob([compressedBytes]).stream().pipeThrough(new DecompressionStream('gzip')); | |
const decompressedArrayBuffer = await new Response(decompressedStream).arrayBuffer(); | |
const decoder = new TextDecoder(); | |
const decompressedStr = decoder.decode(decompressedArrayBuffer); | |
return decompressedStr; | |
} | |
// The configuration string | |
let configStr = '{"andLogicTags":[{"intraTagLogic":"OR","andTags":["caas:content-type/report","caas:content-type/video","caas:content-type/webinar","caas:content-type/infographic","caas:content-type/ebook","caas:content-type/guide","caas:content-type/demo","caas:content-type/blog","caas:content-type/customer-story","caas:content-type/event-session","caas:content-type/demos-and-videos","caas:content-type/podcast","caas:content-type/customer-blog"]}],"cardStyle":"1:2","endpoint":"14257-chimera.adobeioruntime.net/api/v1/web/chimera-0.0.1/collection","excludeTags":["caas:events/max","caas:content-type/product"],"excludedCards":[{"contentId":"64c7ad2e-7bf1-5e7f-8619-c66f27371445"},{"contentId":"0dfd22b4-56d1-5ce5-ad6e-b58c5ef608d5"},{"contentId":"d69bc403-58d5-5fcf-9fad-4d006f6b96d9"},{"contentId":"bfc32f06-6e6a-5f99-b252-48c1410be5a3"},{"contentId":"c80d9edd-8c6d-5fc2-97f1-445f53e49188"},{"contentId":"97380bce-0678-5e02-900e-82957e2c4afe"}],"layoutType":"3up","loadMoreBtnStyle":"over-background","paginationEnabled":true,"paginationQuantityShown":true,"paginationUseTheme3":true,"placeholderUrl":"https://main--bacom--adobecom.hlx.live/caas/mappings","resultsPerPage":"9","searchFields":["overlays.banner.description","contentArea.description","contentArea.detailText","contentArea.title","overlays.label.description"],"setCardBorders":true,"showFilters":true,"showSearch":true,"showTotalResults":true,"sortEnablePopup":true,"source":["bacom"],"totalCardsToShow":"4500","filters":[{"filterTag":["caas:products"],"openedOnLoad":"","icon":"","excludeTags":["caas:products/magento-commerce"]},{"filterTag":["caas:content-type"],"openedOnLoad":"","icon":"","excludeTags":["caas:content-type/customer-story"]},{"filterTag":["caas:topic"],"openedOnLoad":"","icon":"","excludeTags":["caas:topic/enterprise-work-management"]},{"filterTag":["caas:industry"],"openedOnLoad":"","icon":"","excludeTags":[]}],"sortDateAsc":true,"sortDateDesc":true,"sortTitleAsc":true,"sortTitleDesc":true,"collectionName":"All resources","doNotLazyLoad":true,"autoCountryLang":true,"showIds":true,"filterBuildPanel":"custom","filtersCustom":[{"filterTag":"","openedOnLoad":"","icon":"","excludeTags":"","group":"{customGroupA}","filtersCustomItems":[{"filtersCustomLabel":"Advertising","customFilterTag":["caas:products/adobe-advertising-cloud"]},{"filtersCustomLabel":"Analytics","customFilterTag":["caas:products/adobe-analytics"]},{"filtersCustomLabel":"Audience Manager","customFilterTag":["caas:products/adobe-audience-manager"]},{"filtersCustomLabel":"Campaign","customFilterTag":["caas:products/adobe-campaign"]},{"filtersCustomLabel":"Commerce","customFilterTag":["caas:products/adobe-commerce"]},{"filtersCustomLabel":"Customer Journey Analytics","customFilterTag":["caas:products/customer-journey-analytics"]},{"filtersCustomLabel":"Experience Manager Assets","customFilterTag":["caas:products/adobe-experience-manager-assets"]},{"filtersCustomLabel":"Experience Manager Forms","customFilterTag":["caas:products/adobe-experience-manager-forms"]},{"filtersCustomLabel":"Experience Manager Guides","customFilterTag":["caas:products/experience-manager-guides"]},{"filtersCustomLabel":"Experience Manager Sites","customFilterTag":["caas:products/adobe-experience-manager-sites"]},{"filtersCustomLabel":"GenStudio","customFilterTag":["caas:products/genstudio"]},{"filtersCustomLabel":"Journey Optimizer","customFilterTag":["caas:products/adobe-journey-optimizer"]},{"filtersCustomLabel":"Journey Optimizer B2B Edition","customFilterTag":["caas:products/adobe-journey-optimizer-b2b-edition"]},{"filtersCustomLabel":"Learning Manager","customFilterTag":["caas:products/learning-manager"]},{"filtersCustomLabel":"Marketo Engage","customFilterTag":["caas:products/marketo-engage-bizible"]},{"filtersCustomLabel":"Mix Modeler","customFilterTag":["caas:products/adobe-mix-modeler"]},{"filtersCustomLabel":"Product Analytics","customFilterTag":["caas:products/adobe-product-analytics"]},{"filtersCustomLabel":"Real-Time CDP","customFilterTag":["caas:products/adobe-real-time-cdp"]},{"filtersCustomLabel":"Target","customFilterTag":["caas:products/adobe-target"]},{"filtersCustomLabel":"Workfront","customFilterTag":["caas:products/workfront"]}]},{"filterTag":"","openedOnLoad":"","icon":"","excludeTags":"","group":"{customGroupB}","filtersCustomItems":[{"filtersCustomLabel":"{customFilterB1}","customFilterTag":["caas:content-type/blog"]},{"filtersCustomLabel":"{customFilterB2}","customFilterTag":["caas:content-type/customer-story"]},{"filtersCustomLabel":"{customFilterB3}","customFilterTag":["caas:content-type/demo"]},{"filtersCustomLabel":"{customFilterB4}","customFilterTag":["caas:content-type/demos-and-videos"]},{"filtersCustomLabel":"{customFilterB5}","customFilterTag":["caas:content-type/ebook"]},{"filtersCustomLabel":"{customFilterB6}","customFilterTag":["caas:content-type/event-session"]},{"filtersCustomLabel":"{customFilterB7}","customFilterTag":["caas:content-type/guide"]},{"filtersCustomLabel":"{customFilterB8}","customFilterTag":["caas:content-type/infographic"]},{"filtersCustomLabel":"{customFilterB9}","customFilterTag":["caas:content-type/podcast"]},{"filtersCustomLabel":"{customFilterB10}","customFilterTag":["caas:content-type/report"]},{"filtersCustomLabel":"{customFilterB11}","customFilterTag":["caas:content-type/video"]},{"filtersCustomLabel":"{customFilterB12}","customFilterTag":["caas:content-type/webinar"]}]},{"group":"{customGroupC}","filtersCustomItems":[{"filtersCustomLabel":"{customFilterC1}","customFilterTag":["caas:industry/automotive-&-mobility"]},{"filtersCustomLabel":"{customFilterC2}","customFilterTag":["caas:industry/consumer-goods"]},{"filtersCustomLabel":"{customFilterC3}","customFilterTag":["caas:industry/financial-services"]},{"filtersCustomLabel":"{customFilterC4}","customFilterTag":["caas:industry/government"]},{"filtersCustomLabel":"{customFilterC5}","customFilterTag":["caas:industry/healthcare"]},{"filtersCustomLabel":"{customFilterC6}","customFilterTag":["caas:industry/high-tech"]},{"filtersCustomLabel":"{customFilterC7}","customFilterTag":["caas:industry/manufacturing"]},{"filtersCustomLabel":"{customFilterC8}","customFilterTag":["caas:industry/media-and-entertainment"]},{"filtersCustomLabel":"{customFilterC9}","customFilterTag":["caas:industry/public-sector"]},{"filtersCustomLabel":"{customFilterC10}","customFilterTag":["caas:industry/retail"]},{"filtersCustomLabel":"{customFilterC11}","customFilterTag":["caas:industry/telecommunications"]},{"filtersCustomLabel":"{customFilterC12}","customFilterTag":["caas:industry/travel-and-hospitality"]}],"openedOnLoad":""}],"hideDateInterval":true}'; | |
// Test function for Base85 encoding and decoding | |
async function testBase85Encoding() { | |
// Compress the input string | |
const compressedBytes = await compressString(configStr); | |
console.log('compressedBytes:', compressedBytes); | |
// Encode using Base85 with URL-safe characters | |
const base85Encoded = base85Encode(compressedBytes); | |
console.log(`Base85 encoded length: ${base85Encoded.length} characters`); | |
console.log(`Base85 encoded string: ${base85Encoded}`); | |
// Decode using Base85 | |
const decodedBytes = base85Decode(base85Encoded); | |
// Verify that the decoded bytes match the original compressed bytes | |
const arraysMatch = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]); | |
const bytesMatch = arraysMatch(compressedBytes, decodedBytes); | |
console.log(`Base85 encoding and decoding bytes match: ${bytesMatch}`); | |
if (!bytesMatch) { | |
console.error('Mismatch between compressed bytes and decoded bytes.'); | |
return; | |
} | |
// Decompress the decoded bytes | |
const decompressedStr = await decompressBytes(decodedBytes); | |
// Verify that the decompressed string matches the original string | |
const success = configStr === decompressedStr; | |
console.log(`Base85 decoding and decompression successful: ${success}`); | |
} | |
// The below configStr test cases have been verified and working. | |
// Commented them out so sample code focuses on BACOM specific use case. | |
// let configStr = `{ | |
// "title": "多言語テスト", | |
// "description": "This is a test with multiple languages: English, 中文, العربية, हिन्दी, Español.", | |
// "emojiTest": "Here are some emojis: 😀🚀🌟🍕", | |
// "specialChars": "Special characters: ~!@#$%^&*()_+-=[]{}|;':\",./<>?", | |
// "nested": { | |
// "chinese": "汉字测试", | |
// "japanese": "日本語のテスト", | |
// "arabic": "اختبار باللغة العربية", | |
// "hindi": "हिंदी में परीक्षण", | |
// "spanish": "Prueba en español" | |
// } | |
// }`; | |
// let configStr = `{ | |
// "surrogatePairs": "𝌆𠀋𠁧", // Characters outside the BMP (Basic Multilingual Plane) | |
// "controlChars": "Text with control chars:\u0000\u0001\u0002\u0003", | |
// "nullByteTest": "String with null byte\0in the middle" | |
// }`; | |
// Run the updated Base85 encoding test | |
testBase85Encoding(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment