1package goja
2
3import (
4	"fmt"
5	"hash/maphash"
6	"io"
7	"math"
8	"reflect"
9	"strconv"
10	"strings"
11
12	"github.com/dop251/goja/unistring"
13)
14
15type asciiString string
16
17type asciiRuneReader struct {
18	s   asciiString
19	pos int
20}
21
22func (rr *asciiRuneReader) ReadRune() (r rune, size int, err error) {
23	if rr.pos < len(rr.s) {
24		r = rune(rr.s[rr.pos])
25		size = 1
26		rr.pos++
27	} else {
28		err = io.EOF
29	}
30	return
31}
32
33func (s asciiString) reader(start int) io.RuneReader {
34	return &asciiRuneReader{
35		s: s[start:],
36	}
37}
38
39func (s asciiString) utf16Reader(start int) io.RuneReader {
40	return s.reader(start)
41}
42
43func (s asciiString) utf16Runes() []rune {
44	runes := make([]rune, len(s))
45	for i := 0; i < len(s); i++ {
46		runes[i] = rune(s[i])
47	}
48	return runes
49}
50
51// ss must be trimmed
52func stringToInt(ss string) (int64, error) {
53	if ss == "" {
54		return 0, nil
55	}
56	if ss == "-0" {
57		return 0, strconv.ErrSyntax
58	}
59	if len(ss) > 2 {
60		switch ss[:2] {
61		case "0x", "0X":
62			return strconv.ParseInt(ss[2:], 16, 64)
63		case "0b", "0B":
64			return strconv.ParseInt(ss[2:], 2, 64)
65		case "0o", "0O":
66			return strconv.ParseInt(ss[2:], 8, 64)
67		}
68	}
69	return strconv.ParseInt(ss, 10, 64)
70}
71
72func (s asciiString) _toInt() (int64, error) {
73	return stringToInt(strings.TrimSpace(string(s)))
74}
75
76func isRangeErr(err error) bool {
77	if err, ok := err.(*strconv.NumError); ok {
78		return err.Err == strconv.ErrRange
79	}
80	return false
81}
82
83func (s asciiString) _toFloat() (float64, error) {
84	ss := strings.TrimSpace(string(s))
85	if ss == "" {
86		return 0, nil
87	}
88	if ss == "-0" {
89		var f float64
90		return -f, nil
91	}
92	f, err := strconv.ParseFloat(ss, 64)
93	if isRangeErr(err) {
94		err = nil
95	}
96	return f, err
97}
98
99func (s asciiString) ToInteger() int64 {
100	if s == "" {
101		return 0
102	}
103	if s == "Infinity" || s == "+Infinity" {
104		return math.MaxInt64
105	}
106	if s == "-Infinity" {
107		return math.MinInt64
108	}
109	i, err := s._toInt()
110	if err != nil {
111		f, err := s._toFloat()
112		if err == nil {
113			return int64(f)
114		}
115	}
116	return i
117}
118
119func (s asciiString) toString() valueString {
120	return s
121}
122
123func (s asciiString) ToString() Value {
124	return s
125}
126
127func (s asciiString) String() string {
128	return string(s)
129}
130
131func (s asciiString) ToFloat() float64 {
132	if s == "" {
133		return 0
134	}
135	if s == "Infinity" || s == "+Infinity" {
136		return math.Inf(1)
137	}
138	if s == "-Infinity" {
139		return math.Inf(-1)
140	}
141	f, err := s._toFloat()
142	if err != nil {
143		i, err := s._toInt()
144		if err == nil {
145			return float64(i)
146		}
147		f = math.NaN()
148	}
149	return f
150}
151
152func (s asciiString) ToBoolean() bool {
153	return s != ""
154}
155
156func (s asciiString) ToNumber() Value {
157	if s == "" {
158		return intToValue(0)
159	}
160	if s == "Infinity" || s == "+Infinity" {
161		return _positiveInf
162	}
163	if s == "-Infinity" {
164		return _negativeInf
165	}
166
167	if i, err := s._toInt(); err == nil {
168		return intToValue(i)
169	}
170
171	if f, err := s._toFloat(); err == nil {
172		return floatToValue(f)
173	}
174
175	return _NaN
176}
177
178func (s asciiString) ToObject(r *Runtime) *Object {
179	return r._newString(s, r.global.StringPrototype)
180}
181
182func (s asciiString) SameAs(other Value) bool {
183	if otherStr, ok := other.(asciiString); ok {
184		return s == otherStr
185	}
186	return false
187}
188
189func (s asciiString) Equals(other Value) bool {
190	if o, ok := other.(asciiString); ok {
191		return s == o
192	}
193
194	if o, ok := other.(valueInt); ok {
195		if o1, e := s._toInt(); e == nil {
196			return o1 == int64(o)
197		}
198		return false
199	}
200
201	if o, ok := other.(valueFloat); ok {
202		return s.ToFloat() == float64(o)
203	}
204
205	if o, ok := other.(valueBool); ok {
206		if o1, e := s._toFloat(); e == nil {
207			return o1 == o.ToFloat()
208		}
209		return false
210	}
211
212	if o, ok := other.(*Object); ok {
213		return s.Equals(o.toPrimitive())
214	}
215	return false
216}
217
218func (s asciiString) StrictEquals(other Value) bool {
219	if otherStr, ok := other.(asciiString); ok {
220		return s == otherStr
221	}
222	return false
223}
224
225func (s asciiString) baseObject(r *Runtime) *Object {
226	ss := r.stringSingleton
227	ss.value = s
228	ss.setLength()
229	return ss.val
230}
231
232func (s asciiString) hash(hash *maphash.Hash) uint64 {
233	_, _ = hash.WriteString(string(s))
234	h := hash.Sum64()
235	hash.Reset()
236	return h
237}
238
239func (s asciiString) charAt(idx int) rune {
240	return rune(s[idx])
241}
242
243func (s asciiString) length() int {
244	return len(s)
245}
246
247func (s asciiString) concat(other valueString) valueString {
248	switch other := other.(type) {
249	case asciiString:
250		b := make([]byte, len(s)+len(other))
251		copy(b, s)
252		copy(b[len(s):], other)
253		return asciiString(b)
254	case unicodeString:
255		b := make([]uint16, len(s)+len(other))
256		b[0] = unistring.BOM
257		for i := 0; i < len(s); i++ {
258			b[i+1] = uint16(s[i])
259		}
260		copy(b[len(s)+1:], other[1:])
261		return unicodeString(b)
262	default:
263		panic(fmt.Errorf("unknown string type: %T", other))
264	}
265}
266
267func (s asciiString) substring(start, end int) valueString {
268	return s[start:end]
269}
270
271func (s asciiString) compareTo(other valueString) int {
272	switch other := other.(type) {
273	case asciiString:
274		return strings.Compare(string(s), string(other))
275	case unicodeString:
276		return strings.Compare(string(s), other.String())
277	default:
278		panic(fmt.Errorf("unknown string type: %T", other))
279	}
280}
281
282func (s asciiString) index(substr valueString, start int) int {
283	if substr, ok := substr.(asciiString); ok {
284		p := strings.Index(string(s[start:]), string(substr))
285		if p >= 0 {
286			return p + start
287		}
288	}
289	return -1
290}
291
292func (s asciiString) lastIndex(substr valueString, pos int) int {
293	if substr, ok := substr.(asciiString); ok {
294		end := pos + len(substr)
295		var ss string
296		if end > len(s) {
297			ss = string(s)
298		} else {
299			ss = string(s[:end])
300		}
301		return strings.LastIndex(ss, string(substr))
302	}
303	return -1
304}
305
306func (s asciiString) toLower() valueString {
307	return asciiString(strings.ToLower(string(s)))
308}
309
310func (s asciiString) toUpper() valueString {
311	return asciiString(strings.ToUpper(string(s)))
312}
313
314func (s asciiString) toTrimmedUTF8() string {
315	return strings.TrimSpace(string(s))
316}
317
318func (s asciiString) string() unistring.String {
319	return unistring.String(s)
320}
321
322func (s asciiString) Export() interface{} {
323	return string(s)
324}
325
326func (s asciiString) ExportType() reflect.Type {
327	return reflectTypeString
328}
329