-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathencode.js
87 lines (78 loc) · 2.74 KB
/
encode.js
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
84
85
86
87
import { normalizeDelimiters, defaultDelimiters } from "./delimiters.js"
// todo: tests for all this
// todo: maybe a makeEncdec
export const makeEncoders = (delims, ...rest) => {
const delimiters = normalizeDelimiters(delims)
return _makeEncoders(delimiters, ...rest)
}
// assumes delimiters are normalized
export const _makeEncoders = (delimiters, fencelengthlimit = 15) => {
const {fencer, escaper, opener, closer} = delimiters
const escape = (str) => {
let ret = ''
let j = 0
for (let i = 0; i < str.length; ++i) {
const c = str[i]
if (c === opener || c === closer || c === escaper) {
ret += str.slice(j, i) + escaper
j = i
}
}
return ret + str.slice(j)
}
/**
* Assumes delimiters are normalized
* Trusts that fencelength is correct
*/
const fence = (str, fencelength) => {
if (fencelength % 2 === 0 || fencelength > fencelengthlimit) throw SyntaxError(`Expected fencelength to be odd and <= ${fencelengthlimit} but got ${fencelength}!`)
return _fence(str, fencelength)
}
const _fence = (str, fencelength) => {
const fence = Array.from({length: fencelength}).fill(escaper).join('')
return `${fence}${fencer}${str}${fencer}${fence}`
}
const needsEscaping = (str) => {
for (let i = 0; i < str.length; ++i) {
const c = str[i]
if (c === opener || c === closer || c === escaper) {
return true
}
}
return false
}
// does at most 2 passes over the str
// ?todo: could be "optimized" (would have to benchmark) to check if needsEscaping while going thru the string instead of doing a sepatate pass
const smartEscape = (str, couldbelast = true) => {
if (needsEscaping(str) === false) return str
let conflictfencestartindex = -1
let maxfenlen = 0
for (let i = 0; i < str.length; ++i) {
const c = str[i]
if (conflictfencestartindex === -1) {
if (c === fencer) {
conflictfencestartindex = i + 1
}
}
else if (c !== escaper) {
if (c === opener || c === closer) {
const currfenlen = i - conflictfencestartindex
if (currfenlen > maxfenlen) maxfenlen = currfenlen
}
conflictfencestartindex = -1
}
}
if (couldbelast && conflictfencestartindex !== -1) {
// the string ends in '`````
const currfenlen = str.length - conflictfencestartindex
if (currfenlen > maxfenlen) maxfenlen = currfenlen
}
// fencelength must be ODD
if (maxfenlen % 2 === 1) maxfenlen += 1
const fencelength = maxfenlen + 1
if (fencelength >= fencelengthlimit) return escape(str)
return _fence(str, fencelength)
}
return {escape, fence, smartEscape}
}
export const {escape, fence, smartEscape} = _makeEncoders(defaultDelimiters)