1
2
3
4
5
6
7 package bsoncore
8
9 import (
10 "bytes"
11 "encoding/base64"
12 "fmt"
13 "math"
14 "sort"
15 "strconv"
16 "strings"
17 "time"
18 "unicode/utf8"
19
20 "go.mongodb.org/mongo-driver/bson/bsontype"
21 "go.mongodb.org/mongo-driver/bson/primitive"
22 )
23
24
25 type ElementTypeError struct {
26 Method string
27 Type bsontype.Type
28 }
29
30
31 func (ete ElementTypeError) Error() string {
32 return "Call of " + ete.Method + " on " + ete.Type.String() + " type"
33 }
34
35
36 type Value struct {
37 Type bsontype.Type
38 Data []byte
39 }
40
41
42 func (v Value) Validate() error {
43 _, _, valid := readValue(v.Data, v.Type)
44 if !valid {
45 return NewInsufficientBytesError(v.Data, v.Data)
46 }
47 return nil
48 }
49
50
51 func (v Value) IsNumber() bool {
52 switch v.Type {
53 case bsontype.Double, bsontype.Int32, bsontype.Int64, bsontype.Decimal128:
54 return true
55 default:
56 return false
57 }
58 }
59
60
61
62 func (v Value) AsInt32() int32 {
63 if !v.IsNumber() {
64 panic(ElementTypeError{"bsoncore.Value.AsInt32", v.Type})
65 }
66 var i32 int32
67 switch v.Type {
68 case bsontype.Double:
69 f64, _, ok := ReadDouble(v.Data)
70 if !ok {
71 panic(NewInsufficientBytesError(v.Data, v.Data))
72 }
73 i32 = int32(f64)
74 case bsontype.Int32:
75 var ok bool
76 i32, _, ok = ReadInt32(v.Data)
77 if !ok {
78 panic(NewInsufficientBytesError(v.Data, v.Data))
79 }
80 case bsontype.Int64:
81 i64, _, ok := ReadInt64(v.Data)
82 if !ok {
83 panic(NewInsufficientBytesError(v.Data, v.Data))
84 }
85 i32 = int32(i64)
86 case bsontype.Decimal128:
87 panic(ElementTypeError{"bsoncore.Value.AsInt32", v.Type})
88 }
89 return i32
90 }
91
92
93
94 func (v Value) AsInt32OK() (int32, bool) {
95 if !v.IsNumber() {
96 return 0, false
97 }
98 var i32 int32
99 switch v.Type {
100 case bsontype.Double:
101 f64, _, ok := ReadDouble(v.Data)
102 if !ok {
103 return 0, false
104 }
105 i32 = int32(f64)
106 case bsontype.Int32:
107 var ok bool
108 i32, _, ok = ReadInt32(v.Data)
109 if !ok {
110 return 0, false
111 }
112 case bsontype.Int64:
113 i64, _, ok := ReadInt64(v.Data)
114 if !ok {
115 return 0, false
116 }
117 i32 = int32(i64)
118 case bsontype.Decimal128:
119 return 0, false
120 }
121 return i32, true
122 }
123
124
125
126 func (v Value) AsInt64() int64 {
127 if !v.IsNumber() {
128 panic(ElementTypeError{"bsoncore.Value.AsInt64", v.Type})
129 }
130 var i64 int64
131 switch v.Type {
132 case bsontype.Double:
133 f64, _, ok := ReadDouble(v.Data)
134 if !ok {
135 panic(NewInsufficientBytesError(v.Data, v.Data))
136 }
137 i64 = int64(f64)
138 case bsontype.Int32:
139 var ok bool
140 i32, _, ok := ReadInt32(v.Data)
141 if !ok {
142 panic(NewInsufficientBytesError(v.Data, v.Data))
143 }
144 i64 = int64(i32)
145 case bsontype.Int64:
146 var ok bool
147 i64, _, ok = ReadInt64(v.Data)
148 if !ok {
149 panic(NewInsufficientBytesError(v.Data, v.Data))
150 }
151 case bsontype.Decimal128:
152 panic(ElementTypeError{"bsoncore.Value.AsInt64", v.Type})
153 }
154 return i64
155 }
156
157
158
159 func (v Value) AsInt64OK() (int64, bool) {
160 if !v.IsNumber() {
161 return 0, false
162 }
163 var i64 int64
164 switch v.Type {
165 case bsontype.Double:
166 f64, _, ok := ReadDouble(v.Data)
167 if !ok {
168 return 0, false
169 }
170 i64 = int64(f64)
171 case bsontype.Int32:
172 var ok bool
173 i32, _, ok := ReadInt32(v.Data)
174 if !ok {
175 return 0, false
176 }
177 i64 = int64(i32)
178 case bsontype.Int64:
179 var ok bool
180 i64, _, ok = ReadInt64(v.Data)
181 if !ok {
182 return 0, false
183 }
184 case bsontype.Decimal128:
185 return 0, false
186 }
187 return i64, true
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 func (v Value) Equal(v2 Value) bool {
204 if v.Type != v2.Type {
205 return false
206 }
207
208 return bytes.Equal(v.Data, v2.Data)
209 }
210
211
212
213 func (v Value) String() string {
214 switch v.Type {
215 case bsontype.Double:
216 f64, ok := v.DoubleOK()
217 if !ok {
218 return ""
219 }
220 return fmt.Sprintf(`{"$numberDouble":"%s"}`, formatDouble(f64))
221 case bsontype.String:
222 str, ok := v.StringValueOK()
223 if !ok {
224 return ""
225 }
226 return escapeString(str)
227 case bsontype.EmbeddedDocument:
228 doc, ok := v.DocumentOK()
229 if !ok {
230 return ""
231 }
232 return doc.String()
233 case bsontype.Array:
234 arr, ok := v.ArrayOK()
235 if !ok {
236 return ""
237 }
238 return arr.String()
239 case bsontype.Binary:
240 subtype, data, ok := v.BinaryOK()
241 if !ok {
242 return ""
243 }
244 return fmt.Sprintf(`{"$binary":{"base64":"%s","subType":"%02x"}}`, base64.StdEncoding.EncodeToString(data), subtype)
245 case bsontype.Undefined:
246 return `{"$undefined":true}`
247 case bsontype.ObjectID:
248 oid, ok := v.ObjectIDOK()
249 if !ok {
250 return ""
251 }
252 return fmt.Sprintf(`{"$oid":"%s"}`, oid.Hex())
253 case bsontype.Boolean:
254 b, ok := v.BooleanOK()
255 if !ok {
256 return ""
257 }
258 return strconv.FormatBool(b)
259 case bsontype.DateTime:
260 dt, ok := v.DateTimeOK()
261 if !ok {
262 return ""
263 }
264 return fmt.Sprintf(`{"$date":{"$numberLong":"%d"}}`, dt)
265 case bsontype.Null:
266 return "null"
267 case bsontype.Regex:
268 pattern, options, ok := v.RegexOK()
269 if !ok {
270 return ""
271 }
272 return fmt.Sprintf(
273 `{"$regularExpression":{"pattern":%s,"options":"%s"}}`,
274 escapeString(pattern), sortStringAlphebeticAscending(options),
275 )
276 case bsontype.DBPointer:
277 ns, pointer, ok := v.DBPointerOK()
278 if !ok {
279 return ""
280 }
281 return fmt.Sprintf(`{"$dbPointer":{"$ref":%s,"$id":{"$oid":"%s"}}}`, escapeString(ns), pointer.Hex())
282 case bsontype.JavaScript:
283 js, ok := v.JavaScriptOK()
284 if !ok {
285 return ""
286 }
287 return fmt.Sprintf(`{"$code":%s}`, escapeString(js))
288 case bsontype.Symbol:
289 symbol, ok := v.SymbolOK()
290 if !ok {
291 return ""
292 }
293 return fmt.Sprintf(`{"$symbol":%s}`, escapeString(symbol))
294 case bsontype.CodeWithScope:
295 code, scope, ok := v.CodeWithScopeOK()
296 if !ok {
297 return ""
298 }
299 return fmt.Sprintf(`{"$code":%s,"$scope":%s}`, code, scope)
300 case bsontype.Int32:
301 i32, ok := v.Int32OK()
302 if !ok {
303 return ""
304 }
305 return fmt.Sprintf(`{"$numberInt":"%d"}`, i32)
306 case bsontype.Timestamp:
307 t, i, ok := v.TimestampOK()
308 if !ok {
309 return ""
310 }
311 return fmt.Sprintf(`{"$timestamp":{"t":%v,"i":%v}}`, t, i)
312 case bsontype.Int64:
313 i64, ok := v.Int64OK()
314 if !ok {
315 return ""
316 }
317 return fmt.Sprintf(`{"$numberLong":"%d"}`, i64)
318 case bsontype.Decimal128:
319 d128, ok := v.Decimal128OK()
320 if !ok {
321 return ""
322 }
323 return fmt.Sprintf(`{"$numberDecimal":"%s"}`, d128.String())
324 case bsontype.MinKey:
325 return `{"$minKey":1}`
326 case bsontype.MaxKey:
327 return `{"$maxKey":1}`
328 default:
329 return ""
330 }
331 }
332
333
334
335 func (v Value) DebugString() string {
336 switch v.Type {
337 case bsontype.String:
338 str, ok := v.StringValueOK()
339 if !ok {
340 return "<malformed>"
341 }
342 return escapeString(str)
343 case bsontype.EmbeddedDocument:
344 doc, ok := v.DocumentOK()
345 if !ok {
346 return "<malformed>"
347 }
348 return doc.DebugString()
349 case bsontype.Array:
350 arr, ok := v.ArrayOK()
351 if !ok {
352 return "<malformed>"
353 }
354 return arr.DebugString()
355 case bsontype.CodeWithScope:
356 code, scope, ok := v.CodeWithScopeOK()
357 if !ok {
358 return ""
359 }
360 return fmt.Sprintf(`{"$code":%s,"$scope":%s}`, code, scope.DebugString())
361 default:
362 str := v.String()
363 if str == "" {
364 return "<malformed>"
365 }
366 return str
367 }
368 }
369
370
371
372 func (v Value) Double() float64 {
373 if v.Type != bsontype.Double {
374 panic(ElementTypeError{"bsoncore.Value.Double", v.Type})
375 }
376 f64, _, ok := ReadDouble(v.Data)
377 if !ok {
378 panic(NewInsufficientBytesError(v.Data, v.Data))
379 }
380 return f64
381 }
382
383
384 func (v Value) DoubleOK() (float64, bool) {
385 if v.Type != bsontype.Double {
386 return 0, false
387 }
388 f64, _, ok := ReadDouble(v.Data)
389 if !ok {
390 return 0, false
391 }
392 return f64, true
393 }
394
395
396
397
398
399
400 func (v Value) StringValue() string {
401 if v.Type != bsontype.String {
402 panic(ElementTypeError{"bsoncore.Value.StringValue", v.Type})
403 }
404 str, _, ok := ReadString(v.Data)
405 if !ok {
406 panic(NewInsufficientBytesError(v.Data, v.Data))
407 }
408 return str
409 }
410
411
412
413 func (v Value) StringValueOK() (string, bool) {
414 if v.Type != bsontype.String {
415 return "", false
416 }
417 str, _, ok := ReadString(v.Data)
418 if !ok {
419 return "", false
420 }
421 return str, true
422 }
423
424
425
426 func (v Value) Document() Document {
427 if v.Type != bsontype.EmbeddedDocument {
428 panic(ElementTypeError{"bsoncore.Value.Document", v.Type})
429 }
430 doc, _, ok := ReadDocument(v.Data)
431 if !ok {
432 panic(NewInsufficientBytesError(v.Data, v.Data))
433 }
434 return doc
435 }
436
437
438
439 func (v Value) DocumentOK() (Document, bool) {
440 if v.Type != bsontype.EmbeddedDocument {
441 return nil, false
442 }
443 doc, _, ok := ReadDocument(v.Data)
444 if !ok {
445 return nil, false
446 }
447 return doc, true
448 }
449
450
451
452 func (v Value) Array() Array {
453 if v.Type != bsontype.Array {
454 panic(ElementTypeError{"bsoncore.Value.Array", v.Type})
455 }
456 arr, _, ok := ReadArray(v.Data)
457 if !ok {
458 panic(NewInsufficientBytesError(v.Data, v.Data))
459 }
460 return arr
461 }
462
463
464
465 func (v Value) ArrayOK() (Array, bool) {
466 if v.Type != bsontype.Array {
467 return nil, false
468 }
469 arr, _, ok := ReadArray(v.Data)
470 if !ok {
471 return nil, false
472 }
473 return arr, true
474 }
475
476
477
478 func (v Value) Binary() (subtype byte, data []byte) {
479 if v.Type != bsontype.Binary {
480 panic(ElementTypeError{"bsoncore.Value.Binary", v.Type})
481 }
482 subtype, data, _, ok := ReadBinary(v.Data)
483 if !ok {
484 panic(NewInsufficientBytesError(v.Data, v.Data))
485 }
486 return subtype, data
487 }
488
489
490
491 func (v Value) BinaryOK() (subtype byte, data []byte, ok bool) {
492 if v.Type != bsontype.Binary {
493 return 0x00, nil, false
494 }
495 subtype, data, _, ok = ReadBinary(v.Data)
496 if !ok {
497 return 0x00, nil, false
498 }
499 return subtype, data, true
500 }
501
502
503
504 func (v Value) ObjectID() primitive.ObjectID {
505 if v.Type != bsontype.ObjectID {
506 panic(ElementTypeError{"bsoncore.Value.ObjectID", v.Type})
507 }
508 oid, _, ok := ReadObjectID(v.Data)
509 if !ok {
510 panic(NewInsufficientBytesError(v.Data, v.Data))
511 }
512 return oid
513 }
514
515
516
517 func (v Value) ObjectIDOK() (primitive.ObjectID, bool) {
518 if v.Type != bsontype.ObjectID {
519 return primitive.ObjectID{}, false
520 }
521 oid, _, ok := ReadObjectID(v.Data)
522 if !ok {
523 return primitive.ObjectID{}, false
524 }
525 return oid, true
526 }
527
528
529
530 func (v Value) Boolean() bool {
531 if v.Type != bsontype.Boolean {
532 panic(ElementTypeError{"bsoncore.Value.Boolean", v.Type})
533 }
534 b, _, ok := ReadBoolean(v.Data)
535 if !ok {
536 panic(NewInsufficientBytesError(v.Data, v.Data))
537 }
538 return b
539 }
540
541
542
543 func (v Value) BooleanOK() (bool, bool) {
544 if v.Type != bsontype.Boolean {
545 return false, false
546 }
547 b, _, ok := ReadBoolean(v.Data)
548 if !ok {
549 return false, false
550 }
551 return b, true
552 }
553
554
555
556 func (v Value) DateTime() int64 {
557 if v.Type != bsontype.DateTime {
558 panic(ElementTypeError{"bsoncore.Value.DateTime", v.Type})
559 }
560 dt, _, ok := ReadDateTime(v.Data)
561 if !ok {
562 panic(NewInsufficientBytesError(v.Data, v.Data))
563 }
564 return dt
565 }
566
567
568
569 func (v Value) DateTimeOK() (int64, bool) {
570 if v.Type != bsontype.DateTime {
571 return 0, false
572 }
573 dt, _, ok := ReadDateTime(v.Data)
574 if !ok {
575 return 0, false
576 }
577 return dt, true
578 }
579
580
581
582 func (v Value) Time() time.Time {
583 if v.Type != bsontype.DateTime {
584 panic(ElementTypeError{"bsoncore.Value.Time", v.Type})
585 }
586 dt, _, ok := ReadDateTime(v.Data)
587 if !ok {
588 panic(NewInsufficientBytesError(v.Data, v.Data))
589 }
590 return time.Unix(dt/1000, dt%1000*1000000)
591 }
592
593
594
595 func (v Value) TimeOK() (time.Time, bool) {
596 if v.Type != bsontype.DateTime {
597 return time.Time{}, false
598 }
599 dt, _, ok := ReadDateTime(v.Data)
600 if !ok {
601 return time.Time{}, false
602 }
603 return time.Unix(dt/1000, dt%1000*1000000), true
604 }
605
606
607
608 func (v Value) Regex() (pattern, options string) {
609 if v.Type != bsontype.Regex {
610 panic(ElementTypeError{"bsoncore.Value.Regex", v.Type})
611 }
612 pattern, options, _, ok := ReadRegex(v.Data)
613 if !ok {
614 panic(NewInsufficientBytesError(v.Data, v.Data))
615 }
616 return pattern, options
617 }
618
619
620
621 func (v Value) RegexOK() (pattern, options string, ok bool) {
622 if v.Type != bsontype.Regex {
623 return "", "", false
624 }
625 pattern, options, _, ok = ReadRegex(v.Data)
626 if !ok {
627 return "", "", false
628 }
629 return pattern, options, true
630 }
631
632
633
634 func (v Value) DBPointer() (string, primitive.ObjectID) {
635 if v.Type != bsontype.DBPointer {
636 panic(ElementTypeError{"bsoncore.Value.DBPointer", v.Type})
637 }
638 ns, pointer, _, ok := ReadDBPointer(v.Data)
639 if !ok {
640 panic(NewInsufficientBytesError(v.Data, v.Data))
641 }
642 return ns, pointer
643 }
644
645
646
647 func (v Value) DBPointerOK() (string, primitive.ObjectID, bool) {
648 if v.Type != bsontype.DBPointer {
649 return "", primitive.ObjectID{}, false
650 }
651 ns, pointer, _, ok := ReadDBPointer(v.Data)
652 if !ok {
653 return "", primitive.ObjectID{}, false
654 }
655 return ns, pointer, true
656 }
657
658
659
660 func (v Value) JavaScript() string {
661 if v.Type != bsontype.JavaScript {
662 panic(ElementTypeError{"bsoncore.Value.JavaScript", v.Type})
663 }
664 js, _, ok := ReadJavaScript(v.Data)
665 if !ok {
666 panic(NewInsufficientBytesError(v.Data, v.Data))
667 }
668 return js
669 }
670
671
672
673 func (v Value) JavaScriptOK() (string, bool) {
674 if v.Type != bsontype.JavaScript {
675 return "", false
676 }
677 js, _, ok := ReadJavaScript(v.Data)
678 if !ok {
679 return "", false
680 }
681 return js, true
682 }
683
684
685
686 func (v Value) Symbol() string {
687 if v.Type != bsontype.Symbol {
688 panic(ElementTypeError{"bsoncore.Value.Symbol", v.Type})
689 }
690 symbol, _, ok := ReadSymbol(v.Data)
691 if !ok {
692 panic(NewInsufficientBytesError(v.Data, v.Data))
693 }
694 return symbol
695 }
696
697
698
699 func (v Value) SymbolOK() (string, bool) {
700 if v.Type != bsontype.Symbol {
701 return "", false
702 }
703 symbol, _, ok := ReadSymbol(v.Data)
704 if !ok {
705 return "", false
706 }
707 return symbol, true
708 }
709
710
711
712 func (v Value) CodeWithScope() (string, Document) {
713 if v.Type != bsontype.CodeWithScope {
714 panic(ElementTypeError{"bsoncore.Value.CodeWithScope", v.Type})
715 }
716 code, scope, _, ok := ReadCodeWithScope(v.Data)
717 if !ok {
718 panic(NewInsufficientBytesError(v.Data, v.Data))
719 }
720 return code, scope
721 }
722
723
724
725 func (v Value) CodeWithScopeOK() (string, Document, bool) {
726 if v.Type != bsontype.CodeWithScope {
727 return "", nil, false
728 }
729 code, scope, _, ok := ReadCodeWithScope(v.Data)
730 if !ok {
731 return "", nil, false
732 }
733 return code, scope, true
734 }
735
736
737
738 func (v Value) Int32() int32 {
739 if v.Type != bsontype.Int32 {
740 panic(ElementTypeError{"bsoncore.Value.Int32", v.Type})
741 }
742 i32, _, ok := ReadInt32(v.Data)
743 if !ok {
744 panic(NewInsufficientBytesError(v.Data, v.Data))
745 }
746 return i32
747 }
748
749
750
751 func (v Value) Int32OK() (int32, bool) {
752 if v.Type != bsontype.Int32 {
753 return 0, false
754 }
755 i32, _, ok := ReadInt32(v.Data)
756 if !ok {
757 return 0, false
758 }
759 return i32, true
760 }
761
762
763
764 func (v Value) Timestamp() (t, i uint32) {
765 if v.Type != bsontype.Timestamp {
766 panic(ElementTypeError{"bsoncore.Value.Timestamp", v.Type})
767 }
768 t, i, _, ok := ReadTimestamp(v.Data)
769 if !ok {
770 panic(NewInsufficientBytesError(v.Data, v.Data))
771 }
772 return t, i
773 }
774
775
776
777 func (v Value) TimestampOK() (t, i uint32, ok bool) {
778 if v.Type != bsontype.Timestamp {
779 return 0, 0, false
780 }
781 t, i, _, ok = ReadTimestamp(v.Data)
782 if !ok {
783 return 0, 0, false
784 }
785 return t, i, true
786 }
787
788
789
790 func (v Value) Int64() int64 {
791 if v.Type != bsontype.Int64 {
792 panic(ElementTypeError{"bsoncore.Value.Int64", v.Type})
793 }
794 i64, _, ok := ReadInt64(v.Data)
795 if !ok {
796 panic(NewInsufficientBytesError(v.Data, v.Data))
797 }
798 return i64
799 }
800
801
802
803 func (v Value) Int64OK() (int64, bool) {
804 if v.Type != bsontype.Int64 {
805 return 0, false
806 }
807 i64, _, ok := ReadInt64(v.Data)
808 if !ok {
809 return 0, false
810 }
811 return i64, true
812 }
813
814
815
816 func (v Value) Decimal128() primitive.Decimal128 {
817 if v.Type != bsontype.Decimal128 {
818 panic(ElementTypeError{"bsoncore.Value.Decimal128", v.Type})
819 }
820 d128, _, ok := ReadDecimal128(v.Data)
821 if !ok {
822 panic(NewInsufficientBytesError(v.Data, v.Data))
823 }
824 return d128
825 }
826
827
828
829 func (v Value) Decimal128OK() (primitive.Decimal128, bool) {
830 if v.Type != bsontype.Decimal128 {
831 return primitive.Decimal128{}, false
832 }
833 d128, _, ok := ReadDecimal128(v.Data)
834 if !ok {
835 return primitive.Decimal128{}, false
836 }
837 return d128, true
838 }
839
840 var hexChars = "0123456789abcdef"
841
842 func escapeString(s string) string {
843 escapeHTML := true
844 var buf bytes.Buffer
845 buf.WriteByte('"')
846 start := 0
847 for i := 0; i < len(s); {
848 if b := s[i]; b < utf8.RuneSelf {
849 if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
850 i++
851 continue
852 }
853 if start < i {
854 buf.WriteString(s[start:i])
855 }
856 switch b {
857 case '\\', '"':
858 buf.WriteByte('\\')
859 buf.WriteByte(b)
860 case '\n':
861 buf.WriteByte('\\')
862 buf.WriteByte('n')
863 case '\r':
864 buf.WriteByte('\\')
865 buf.WriteByte('r')
866 case '\t':
867 buf.WriteByte('\\')
868 buf.WriteByte('t')
869 case '\b':
870 buf.WriteByte('\\')
871 buf.WriteByte('b')
872 case '\f':
873 buf.WriteByte('\\')
874 buf.WriteByte('f')
875 default:
876
877
878
879
880
881 buf.WriteString(`\u00`)
882 buf.WriteByte(hexChars[b>>4])
883 buf.WriteByte(hexChars[b&0xF])
884 }
885 i++
886 start = i
887 continue
888 }
889 c, size := utf8.DecodeRuneInString(s[i:])
890 if c == utf8.RuneError && size == 1 {
891 if start < i {
892 buf.WriteString(s[start:i])
893 }
894 buf.WriteString(`\ufffd`)
895 i += size
896 start = i
897 continue
898 }
899
900
901
902
903
904
905
906 if c == '\u2028' || c == '\u2029' {
907 if start < i {
908 buf.WriteString(s[start:i])
909 }
910 buf.WriteString(`\u202`)
911 buf.WriteByte(hexChars[c&0xF])
912 i += size
913 start = i
914 continue
915 }
916 i += size
917 }
918 if start < len(s) {
919 buf.WriteString(s[start:])
920 }
921 buf.WriteByte('"')
922 return buf.String()
923 }
924
925 func formatDouble(f float64) string {
926 var s string
927 if math.IsInf(f, 1) {
928 s = "Infinity"
929 } else if math.IsInf(f, -1) {
930 s = "-Infinity"
931 } else if math.IsNaN(f) {
932 s = "NaN"
933 } else {
934
935
936 s = strconv.FormatFloat(f, 'G', -1, 64)
937 if !strings.ContainsRune(s, '.') {
938 s += ".0"
939 }
940 }
941
942 return s
943 }
944
945 type sortableString []rune
946
947 func (ss sortableString) Len() int {
948 return len(ss)
949 }
950
951 func (ss sortableString) Less(i, j int) bool {
952 return ss[i] < ss[j]
953 }
954
955 func (ss sortableString) Swap(i, j int) {
956 oldI := ss[i]
957 ss[i] = ss[j]
958 ss[j] = oldI
959 }
960
961 func sortStringAlphebeticAscending(s string) string {
962 ss := sortableString([]rune(s))
963 sort.Sort(ss)
964 return string([]rune(ss))
965 }
966
View as plain text