-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
622 additions
and
143 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Component } from '@ndn/packet'; | ||
import { assert as assertMod } from '../dep.ts'; | ||
import { name } from '../utils/mod.ts'; | ||
import * as namePattern from './name-pattern.ts'; | ||
import { pattern } from './name-pattern.ts'; | ||
|
||
const assert = assertMod.assert as ((expr: unknown, msg?: string) => void); | ||
const { assertEquals } = assertMod; | ||
|
||
Deno.test('Name Pattern construction', () => { | ||
const pat = pattern`/8=base/<8=peerId:string>/<58=sequence:number>`; | ||
assertEquals(pat.length, 3); | ||
assert((pat[0] as Component).equals(new Component(8, 'base'))); | ||
assertEquals(pat[1] as namePattern.PatternComponent, { | ||
type: 8, | ||
kind: 'string', | ||
tag: 'peerId', | ||
}); | ||
assertEquals(pat[2] as namePattern.PatternComponent, { | ||
type: 58, | ||
kind: 'number', | ||
tag: 'sequence', | ||
}); | ||
|
||
const mapping = {}; | ||
assert(namePattern.match(pat, name`/base/peer-01/seq=${13}`, mapping)); | ||
assertEquals(mapping, { | ||
peerId: 'peer-01', | ||
sequence: 13, | ||
}); | ||
}); | ||
|
||
Deno.test('Make name from patterns', () => { | ||
const pat = pattern`/8=base/<8=peerId:string>/<58=sequence:number>`; | ||
assert( | ||
namePattern.make(pat, { | ||
peerId: 'peer-01', | ||
sequence: 13, | ||
}).equals(name`/base/peer-01/seq=${13}`), | ||
); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,146 @@ | ||
export const pattern = ([_value]: TemplateStringsArray) => { | ||
throw new Error('Not implemented'); | ||
import { Component, Name } from '@ndn/packet'; | ||
import { Encoder, NNI } from '@ndn/tlv'; | ||
|
||
export type PatternKind = 'bytes' | 'number' | 'string'; | ||
export type MatchValue = Uint8Array | number | string; | ||
|
||
export type PatternComponent = { | ||
/** TLV-TYPE. */ | ||
type: number; | ||
/** Pattern's matching variable name */ | ||
tag: string; | ||
/** Pattern value type */ | ||
kind: PatternKind; | ||
}; | ||
|
||
export type Pattern = Array<PatternComponent | Component>; | ||
|
||
export const patternComponentToString = (comp: PatternComponent) => `<${comp.type}=${comp.tag}:${comp.kind}>`; | ||
|
||
export const componentToString = (comp: PatternComponent | Component) => | ||
comp instanceof Component ? comp.toString() : patternComponentToString(comp); | ||
|
||
/** | ||
* Convert a name pattern to string | ||
* @param pat The name pattern | ||
* @returns String representation | ||
*/ | ||
export const toString = (pat: Pattern) => '/' + pat.map(componentToString).join('/'); | ||
|
||
export const matchStep = ( | ||
pattern: PatternComponent | Component, | ||
subject: Component, | ||
mapping: Record<string, MatchValue>, | ||
) => { | ||
if (pattern.type !== subject.type) { | ||
return false; | ||
} | ||
if (pattern instanceof Component) { | ||
return pattern.equals(subject); | ||
} else if (pattern.kind === 'bytes') { | ||
mapping[pattern.tag] = subject.value; | ||
return true; | ||
} else if (pattern.kind === 'string') { | ||
mapping[pattern.tag] = new TextDecoder().decode(subject.value); | ||
This comment has been minimized.
Sorry, something went wrong. |
||
return true; | ||
} else { | ||
try { | ||
mapping[pattern.tag] = NNI.decode(subject.value); | ||
return true; | ||
} catch { | ||
return false; | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Match a given name with a given name pattern, and put matched variables into mapping. | ||
* Digest components are removen. | ||
* | ||
* @param pattern The name pattern to match with. | ||
* @param subject The name to be matched. | ||
* @param mapping The mapping holding matched variables. | ||
* @returns `true` if succeeded, `false` if failed. | ||
* @throws when the component type matches but its value cannot be decoded as specified value kind. | ||
*/ | ||
export const match = ( | ||
pattern: Pattern, | ||
subject: Name, | ||
mapping: Record<string, MatchValue>, | ||
) => { | ||
// Remove automatically added component | ||
// ImplicitSha256DigestComponent(0x01) and ParametersSha256DigestComponent(0x02) | ||
while (subject.length > 0 && subject.at(subject.length - 1).type <= 2) { | ||
subject = subject.getPrefix(subject.length - 1); | ||
} | ||
// Must be equal length | ||
if (subject.length !== pattern.length) { | ||
return false; | ||
} | ||
for (const [i, p] of pattern.entries()) { | ||
if (!matchStep(p, subject.at(i), mapping)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
|
||
export const makeStep = ( | ||
pattern: PatternComponent | Component, | ||
mapping: Record<string, MatchValue>, | ||
) => { | ||
if (pattern instanceof Component) { | ||
return pattern; | ||
} else { | ||
const value = mapping[pattern.tag]; | ||
if (!value) { | ||
throw new Error(`The pattern variable ${pattern.tag} does not exist in the mapping.`); | ||
} | ||
const v = typeof value === 'number' ? Encoder.encode(NNI(value)) : value; | ||
return new Component(pattern.type, v); | ||
} | ||
}; | ||
|
||
/** | ||
* Construct a Name from a given name pattern with variable mapping. | ||
* | ||
* @remarks The value kind of pattern components are not checked. | ||
* @param pattern The input name pattern | ||
* @param mapping The variable mapping | ||
* @returns The constructed name | ||
* @throws if a pattern with a given variable is missing | ||
*/ | ||
export const make = ( | ||
pattern: Pattern, | ||
mapping: Record<string, MatchValue>, | ||
) => new Name(pattern.map((p) => makeStep(p, mapping))); | ||
|
||
export const componentFromString = (value: string) => { | ||
if (value.length === 0) { | ||
return new Component(); | ||
} | ||
if (value[0] !== '<') { | ||
return Component.from(value); | ||
} else { | ||
const matching = /^<(?<type>[0-9]+)=(?<tag>[a-zA-Z0-9$_-]+):(?<kind>bytes|number|string)>$/.exec(value); | ||
if (!matching || !matching.groups) { | ||
throw new Error(`Invalid pattern component: ${value}`); | ||
} | ||
return { | ||
type: parseInt(matching.groups.type), | ||
kind: matching.groups.kind as PatternKind, | ||
tag: matching.groups.tag, | ||
} as PatternComponent; | ||
} | ||
}; | ||
|
||
export const fromString = (value: string) => { | ||
if (value[0] === '/') { | ||
value = value.substring(1); | ||
} | ||
return value.split('/').map(componentFromString) as Pattern; | ||
}; | ||
|
||
export const pattern = ([value]: TemplateStringsArray) => { | ||
return fromString(value); | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Name } from '@ndn/packet'; | ||
import * as namePattern from './name-pattern.ts'; | ||
|
||
export type Node<R> = { | ||
children: Map<number, { | ||
edge: namePattern.PatternComponent; | ||
dest: Node<R>; | ||
}>; | ||
parent: WeakRef<Node<R>> | undefined; | ||
resource: R; | ||
}; | ||
|
||
export type MatchedObject<R> = { | ||
mapping: Record<string, namePattern.MatchValue>; | ||
name: Name; | ||
resource: R; | ||
}; |
Oops, something went wrong.
rhs can be simplified as: