File size: 3,140 Bytes
96af7c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReplayPath = void 0;
class ReplayPath {
    static parse(replayPathStr) {
        const [serializedCount, serializedChanges] = replayPathStr.split(':');
        const counts = this.parseCounts(serializedCount);
        const changes = this.parseChanges(serializedChanges);
        return this.parseOccurences(counts, changes);
    }
    static stringify(replayPath) {
        const occurences = this.countOccurences(replayPath);
        const serializedCount = this.stringifyCounts(occurences);
        const serializedChanges = this.stringifyChanges(occurences);
        return `${serializedCount}:${serializedChanges}`;
    }
    static intToB64(n) {
        if (n < 26)
            return String.fromCharCode(n + 65);
        if (n < 52)
            return String.fromCharCode(n + 97 - 26);
        if (n < 62)
            return String.fromCharCode(n + 48 - 52);
        return String.fromCharCode(n === 62 ? 43 : 47);
    }
    static b64ToInt(c) {
        if (c >= 'a')
            return c.charCodeAt(0) - 97 + 26;
        if (c >= 'A')
            return c.charCodeAt(0) - 65;
        if (c >= '0')
            return c.charCodeAt(0) - 48 + 52;
        return c === '+' ? 62 : 63;
    }
    static countOccurences(replayPath) {
        return replayPath.reduce((counts, cur) => {
            if (counts.length === 0 || counts[counts.length - 1].count === 64 || counts[counts.length - 1].value !== cur)
                counts.push({ value: cur, count: 1 });
            else
                counts[counts.length - 1].count += 1;
            return counts;
        }, []);
    }
    static parseOccurences(counts, changes) {
        const replayPath = [];
        for (let idx = 0; idx !== counts.length; ++idx) {
            const count = counts[idx];
            const value = changes[idx];
            for (let num = 0; num !== count; ++num)
                replayPath.push(value);
        }
        return replayPath;
    }
    static stringifyChanges(occurences) {
        let serializedChanges = '';
        for (let idx = 0; idx < occurences.length; idx += 6) {
            const changesInt = occurences
                .slice(idx, idx + 6)
                .reduceRight((prev, cur) => prev * 2 + (cur.value ? 1 : 0), 0);
            serializedChanges += this.intToB64(changesInt);
        }
        return serializedChanges;
    }
    static parseChanges(serializedChanges) {
        const changesInt = serializedChanges.split('').map((c) => this.b64ToInt(c));
        const changes = [];
        for (let idx = 0; idx !== changesInt.length; ++idx) {
            let current = changesInt[idx];
            for (let n = 0; n !== 6; ++n, current >>= 1) {
                changes.push(current % 2 === 1);
            }
        }
        return changes;
    }
    static stringifyCounts(occurences) {
        return occurences.map(({ count }) => this.intToB64(count - 1)).join('');
    }
    static parseCounts(serializedCount) {
        return serializedCount.split('').map((c) => this.b64ToInt(c) + 1);
    }
}
exports.ReplayPath = ReplayPath;