vtTools/encoding/charmap/encoding.go

257 lines
6.1 KiB
Go

package charmap
import (
"bytes"
"errors"
"io"
"git.sdf.org/CRThaze/vtTools/encoding/codetable"
mnemonic "git.sdf.org/CRThaze/vtTools/encoding/decMSCMnemonics"
equiv "git.sdf.org/CRThaze/vtTools/equivalence"
"golang.org/x/text/encoding"
"golang.org/x/text/transform"
)
var (
ErrUnboundCodepage = errors.New("Codepage is not bound to a codespace")
)
type transformer interface {
transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
}
type Codepage struct {
charSet CharacterSet
codeSpace *codetable.GraphicCodeSpace
}
func NewCodepage(set CharacterSet, graphicTable *codetable.GraphicCodeSpace) Codepage {
return Codepage{
charSet: set,
codeSpace: graphicTable,
}
}
type DynamicCodepage struct {
Codepage
}
func NewDynamicCodepage(set CharacterSet) DynamicCodepage {
e := DynamicCodepage{
Codepage{charSet: set},
}
return e
}
func (p *DynamicCodepage) Bind(graphicTable *codetable.GraphicCodeSpace) {
if graphicTable != nil {
p.codeSpace = graphicTable
return
}
p.codeSpace = nil
}
func (p *DynamicCodepage) IsBoundTo(graphicTable *codetable.GraphicCodeSpace) bool {
if p.codeSpace != nil && p.codeSpace == graphicTable {
return true
}
return false
}
func (p *DynamicCodepage) BoundTo() *codetable.GraphicCodeSpace {
return p.codeSpace
}
type ExtendedCodepage struct {
base Codepage
supplemental Codepage
}
func NewExtendedCodepage(sevenBitSet, eighthBitSet CharacterSet) ExtendedCodepage {
return ExtendedCodepage{
base: Codepage{
charSet: sevenBitSet,
codeSpace: &codetable.GLTable,
},
supplemental: Codepage{
charSet: eighthBitSet,
codeSpace: &codetable.GRTable,
},
}
}
func (p Codepage) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
srcBuf := bytes.NewBuffer(src)
for r, sbn, e := srcBuf.ReadRune(); e != io.EOF; r, sbn, e = srcBuf.ReadRune() {
transBytes := make([]byte, 1, 1)
if e != nil {
err = e
return
} else if r == '\uFFFD' && sbn == 1 {
transBytes[0] = mnemonic.SUB // MSC/ASCII Substituion character.
} else {
transBytes[0] = p.SafeTrans(r)
}
nSrc += sbn
for i, b := range transBytes {
nextIndex := nDst + i
if len(dst) < nextIndex+1 {
err = transform.ErrShortDst
return
}
dst[nDst+i] = b
nDst++
}
}
return
}
func (p ExtendedCodepage) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
srcBuf := bytes.NewBuffer(src)
for r, sbn, e := srcBuf.ReadRune(); e != io.EOF; r, sbn, e = srcBuf.ReadRune() {
transBytes := make([]byte, 1, 1)
if e != nil {
err = e
return
} else if r == '\uFFFD' && sbn == 1 {
transBytes[0] = mnemonic.SUB // MSC/ASCII Substituion character.
} else {
if tb, e := p.base.Trans(equiv.Normalize(r)); e == nil {
transBytes[0] = tb
} else {
transBytes[0] = p.supplemental.SafeTrans(r)
}
}
nSrc += sbn
for i, b := range transBytes {
nextIndex := nDst + i
if len(dst) < nextIndex+1 {
err = transform.ErrShortDst
return
}
dst[nDst+i] = b
nDst++
}
}
return
}
type vtEncEncoder struct {
transform.NopResetter
charmap transformer
}
func (en vtEncEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
return en.charmap.transform(dst, src, atEOF)
}
type vtEncDecoder struct {
transform.NopResetter
charmap Codepage
}
func (d vtEncDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
// TODO: Implement decode by looking up rune in charmap.
return 0, 0, nil
}
func (p Codepage) NewDecoder() *encoding.Decoder {
return &encoding.Decoder{
Transformer: vtEncDecoder{},
}
}
func (p Codepage) NewEncoder() *encoding.Encoder {
return &encoding.Encoder{
Transformer: vtEncEncoder{},
}
}
func (p ExtendedCodepage) NewDecoder() *encoding.Decoder {
return &encoding.Decoder{
Transformer: vtEncDecoder{},
}
}
func (p ExtendedCodepage) NewEncoder() *encoding.Encoder {
return &encoding.Encoder{
Transformer: vtEncEncoder{},
}
}
func (p Codepage) Trans(r rune) (byte, error) {
if p.codeSpace == nil {
return 0, ErrUnboundCodepage
}
if b, ok := decMSCControlFull[r]; ok {
return b, nil
} else if xy := p.charSet.lookup.Get(r); xy != nil {
// Discarding the error since we are confident
// the coordinate will not be out of range.
b, _ = p.codeSpace.Get(xy[0], xy[1])
return b, nil
}
return 0, errors.New("No translation for rune.")
}
func (p Codepage) MustTrans(r rune) byte {
if b, err := p.Trans(r); err == nil {
return b
}
return mnemonic.SUB
}
func (p Codepage) SafeTrans(r rune) byte {
return p.MustTrans(equiv.Normalize(r))
}
// // TransDynamic first calls Trans. If Trans returns an error, then it attempts to find the rune
// // anywhere within the GraphicRepetoir. If it is able to find the rune, it adds the necessary
// // escape sequences to temporarily load the needed character set to properly display the matching
// // symbol. If none can be found, it retuns the same fall-back code-point as MustTrans.
// func (e vtEncEncoder) TransDynamic(r rune) []byte {
// if s, err := vt.Trans(r); err == nil {
// return s
// }
// if vt.graphicRegistry[2].register.designated {
// if vt.graphicRegistry[2].register.set.lookup.Has(r) {
// if bs, err := vt.SingleShiftRune(2, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// }
// if vt.graphicRegistry[3].register.designated {
// if vt.graphicRegistry[3].register.set.lookup.Has(r) {
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// }
// for i, set := range vt.graphicRepetoire {
// if set == nil || !set.lookup.Has(r) {
// continue
// }
// // TODO: Don't leave this in the register.
// if err := vt.DesignateCharSet(i, 3); err != nil {
// panic(err)
// }
// if bs, err := vt.SingleShiftRune(3, r); err == nil {
// return bs
// } else {
// panic(err)
// }
// }
// return UNKNOWN[:]
// }
// // SafeTrans uses an equivalency table for the given rune to determine a similiar rune that is
// // present in the GraphicRepetoir. It then calls TransDynamic with that rune.
// func (e vtEncEncoder) SafeTransDynamic(r rune) []byte {
// return vt.TransDynamic(requiem.Equivalence.Normalize(r))
// }