2025-10-15 10:12:44 +03:00

253 lines
6.1 KiB
Go

package sourcemap
import (
"errors"
"iter"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
)
type Mapping struct {
GeneratedLine int
GeneratedCharacter int
SourceIndex SourceIndex
SourceLine int
SourceCharacter int
NameIndex NameIndex
}
func (m *Mapping) Equals(other *Mapping) bool {
return m == other || m.GeneratedLine == other.GeneratedLine &&
m.GeneratedCharacter == other.GeneratedCharacter &&
m.SourceIndex == other.SourceIndex &&
m.SourceLine == other.SourceLine &&
m.SourceCharacter == other.SourceCharacter &&
m.NameIndex == other.NameIndex
}
func (m *Mapping) IsSourceMapping() bool {
return m.SourceIndex != MissingSource &&
m.SourceLine != MissingLineOrColumn &&
m.SourceCharacter != MissingLineOrColumn
}
const (
MissingSource SourceIndex = -1
MissingName NameIndex = -1
MissingLineOrColumn int = -1
)
type MappingsDecoder struct {
mappings string
done bool
pos int
generatedLine int
generatedCharacter int
sourceIndex SourceIndex
sourceLine int
sourceCharacter int
nameIndex NameIndex
error error
mappingPool core.Pool[Mapping]
}
func DecodeMappings(mappings string) *MappingsDecoder {
return &MappingsDecoder{mappings: mappings}
}
func (d *MappingsDecoder) MappingsString() string {
return d.mappings
}
func (d *MappingsDecoder) Pos() int {
return d.pos
}
func (d *MappingsDecoder) Error() error {
return d.error
}
func (d *MappingsDecoder) State() *Mapping {
return d.captureMapping(true /*hasSource*/, true /*hasName*/)
}
func (d *MappingsDecoder) Values() iter.Seq[*Mapping] {
return func(yield func(*Mapping) bool) {
for value, done := d.Next(); !done; value, done = d.Next() {
if !yield(value) {
break
}
}
}
}
func (d *MappingsDecoder) Next() (value *Mapping, done bool) {
for !d.done && d.pos < len(d.mappings) {
ch := d.mappings[d.pos]
if ch == ';' {
// new line
d.generatedLine++
d.generatedCharacter = 0
d.pos++
continue
}
if ch == ',' {
// Next entry is on same line - no action needed
d.pos++
continue
}
hasSource := false
hasName := false
d.generatedCharacter += d.base64VLQFormatDecode()
if d.hasReportedError() {
return d.stopIterating()
}
if d.generatedCharacter < 0 {
return d.setErrorAndStopIterating("Invalid generatedCharacter found")
}
if !d.isSourceMappingSegmentEnd() {
hasSource = true
d.sourceIndex += SourceIndex(d.base64VLQFormatDecode())
if d.hasReportedError() {
return d.stopIterating()
}
if d.sourceIndex < 0 {
return d.setErrorAndStopIterating("Invalid sourceIndex found")
}
if d.isSourceMappingSegmentEnd() {
return d.setErrorAndStopIterating("Unsupported Format: No entries after sourceIndex")
}
d.sourceLine += d.base64VLQFormatDecode()
if d.hasReportedError() {
return d.stopIterating()
}
if d.sourceLine < 0 {
return d.setErrorAndStopIterating("Invalid sourceLine found")
}
if d.isSourceMappingSegmentEnd() {
return d.setErrorAndStopIterating("Unsupported Format: No entries after sourceLine")
}
d.sourceCharacter += d.base64VLQFormatDecode()
if d.hasReportedError() {
return d.stopIterating()
}
if d.sourceCharacter < 0 {
return d.setErrorAndStopIterating("Invalid sourceCharacter found")
}
if !d.isSourceMappingSegmentEnd() {
hasName = true
d.nameIndex += NameIndex(d.base64VLQFormatDecode())
if d.hasReportedError() {
return d.stopIterating()
}
if d.nameIndex < 0 {
return d.setErrorAndStopIterating("Invalid nameIndex found")
}
if !d.isSourceMappingSegmentEnd() {
return d.setErrorAndStopIterating("Unsupported Error Format: Entries after nameIndex")
}
}
}
return d.captureMapping(hasSource, hasName), false
}
return d.stopIterating()
}
func (d *MappingsDecoder) captureMapping(hasSource bool, hasName bool) *Mapping {
mapping := d.mappingPool.New()
mapping.GeneratedLine = d.generatedLine
mapping.GeneratedCharacter = d.generatedCharacter
mapping.SourceIndex = core.IfElse(hasSource, d.sourceIndex, MissingSource)
mapping.SourceLine = core.IfElse(hasSource, d.sourceLine, MissingLineOrColumn)
mapping.SourceCharacter = core.IfElse(hasSource, d.sourceCharacter, MissingLineOrColumn)
mapping.NameIndex = core.IfElse(hasName, d.nameIndex, MissingName)
return mapping
}
func (d *MappingsDecoder) stopIterating() (*Mapping, bool) {
d.done = true
return nil, true
}
func (d *MappingsDecoder) setError(err string) {
d.error = errors.New(err)
}
func (d *MappingsDecoder) setErrorAndStopIterating(err string) (*Mapping, bool) {
d.setError(err)
return d.stopIterating()
}
func (d *MappingsDecoder) hasReportedError() bool {
return d.error != nil
}
func (d *MappingsDecoder) isSourceMappingSegmentEnd() bool {
return d.pos == len(d.mappings) || d.mappings[d.pos] == ',' || d.mappings[d.pos] == ';'
}
func (d *MappingsDecoder) base64VLQFormatDecode() int {
moreDigits := true
shiftCount := 0
value := 0
for ; moreDigits; d.pos++ {
if d.pos >= len(d.mappings) {
d.setError("Error in decoding base64VLQFormatDecode, past the mapping string")
return -1
}
// 6 digit number
currentByte := base64FormatDecode(d.mappings[d.pos])
if currentByte == -1 {
d.setError("Invalid character in VLQ")
return -1
}
// If msb is set, we still have more bits to continue
moreDigits = (currentByte & 32) != 0
// least significant 5 bits are the next msbs in the final value.
value = value | ((currentByte & 31) << shiftCount)
shiftCount += 5
}
// Least significant bit if 1 represents negative and rest of the msb is actual absolute value
if (value & 1) == 0 {
// + number
value = value >> 1
} else {
// - number
value = value >> 1
value = -value
}
return value
}
func base64FormatDecode(ch byte) int {
switch {
case ch >= 'A' && ch <= 'Z':
return int(ch - 'A')
case ch >= 'a' && ch <= 'z':
return int(ch - 'a' + 26)
case ch >= '0' && ch <= '9':
return int(ch - '0' + 52)
case ch == '+':
return 62
case ch == '/':
return 63
default:
return -1
}
}