Merge lp:~niemeyer/goyaml/stable-maps into lp:~gophers/goyaml/trunk

Proposed by Gustavo Niemeyer
Status: Merged
Merged at revision: 36
Proposed branch: lp:~niemeyer/goyaml/stable-maps
Merge into: lp:~gophers/goyaml/trunk
Diff against target: 218 lines (+176/-2)
3 files modified
encode.go (+4/-1)
encode_test.go (+68/-1)
sorter.go (+104/-0)
To merge this branch: bzr merge lp:~niemeyer/goyaml/stable-maps
Reviewer Review Type Date Requested Status
The Go Language Gophers Pending
Review via email: mp+125431@code.launchpad.net

Description of the change

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Reviewers: mp+125431_code.launchpad.net,

Message:
Please take a look.

Description:
Sort map keys.

https://code.launchpad.net/~niemeyer/goyaml/stable-maps/+merge/125431

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/6551043/

Affected files:
   A [revision details]
   M encode.go
   M encode_test.go
   A sorter.go

lp:~niemeyer/goyaml/stable-maps updated
37. By Gustavo Niemeyer

More tests, after Dave's suggestions.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :
lp:~niemeyer/goyaml/stable-maps updated
38. By Gustavo Niemeyer

Add empty string to test as well, as suggested by Dave.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'encode.go'
--- encode.go 2011-12-15 16:20:57 +0000
+++ encode.go 2012-09-20 09:35:22 +0000
@@ -5,6 +5,7 @@
55
6import (6import (
7 "reflect"7 "reflect"
8 "sort"
8 "strconv"9 "strconv"
9 "unsafe"10 "unsafe"
10)11)
@@ -126,7 +127,9 @@
126127
127func (e *encoder) mapv(tag string, in reflect.Value) {128func (e *encoder) mapv(tag string, in reflect.Value) {
128 e.mappingv(tag, func() {129 e.mappingv(tag, func() {
129 for _, k := range in.MapKeys() {130 keys := keyList(in.MapKeys())
131 sort.Sort(keys)
132 for _, k := range keys {
130 e.marshal("", k)133 e.marshal("", k)
131 e.marshal("", in.MapIndex(k))134 e.marshal("", in.MapIndex(k))
132 }135 }
133136
=== modified file 'encode_test.go'
--- encode_test.go 2011-11-24 19:50:00 +0000
+++ encode_test.go 2012-09-20 09:35:22 +0000
@@ -1,10 +1,12 @@
1package goyaml_test1package goyaml_test
22
3import (3import (
4 "fmt"
4 . "launchpad.net/gocheck"5 . "launchpad.net/gocheck"
5 "launchpad.net/goyaml"6 "launchpad.net/goyaml"
6 "math"7 "math"
7 //"reflect"8 "strconv"
9 "strings"
8)10)
911
10var marshalIntTest = 12312var marshalIntTest = 123
@@ -171,3 +173,68 @@
171 c.Assert(err, IsNil)173 c.Assert(err, IsNil)
172 c.Assert(string(data), Equals, "hello: world!\n")174 c.Assert(string(data), Equals, "hello: world!\n")
173}175}
176
177func (s *S) TestSortedOutput(c *C) {
178 order := []interface{}{
179 false,
180 true,
181 1,
182 uint(1),
183 1.0,
184 1.1,
185 1.2,
186 2,
187 uint(2),
188 2.0,
189 2.1,
190 "",
191 ".1",
192 ".2",
193 ".a",
194 "1",
195 "2",
196 "a!10",
197 "a/2",
198 "a/10",
199 "a~10",
200 "ab/1",
201 "b/1",
202 "b/01",
203 "b/2",
204 "b/02",
205 "b/3",
206 "b/03",
207 "b1",
208 "b01",
209 "b3",
210 "c2.10",
211 "c10.2",
212 "d1",
213 "d12",
214 "d12a",
215 }
216 m := make(map[interface{}]int)
217 for _, k := range order {
218 m[k] = 1
219 }
220 data, err := goyaml.Marshal(m)
221 c.Assert(err, IsNil)
222 out := "\n" + string(data)
223 last := 0
224 for i, k := range order {
225 repr := fmt.Sprint(k)
226 if s, ok := k.(string); ok {
227 if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
228 repr = `"` + repr + `"`
229 }
230 }
231 index := strings.Index(out, "\n"+repr+":")
232 if index == -1 {
233 c.Fatalf("%#v is not in the output: %#v", k, out)
234 }
235 if index < last {
236 c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
237 }
238 last = index
239 }
240}
174241
=== added file 'sorter.go'
--- sorter.go 1970-01-01 00:00:00 +0000
+++ sorter.go 2012-09-20 09:35:22 +0000
@@ -0,0 +1,104 @@
1package goyaml
2
3import (
4 "reflect"
5 "unicode"
6)
7
8type keyList []reflect.Value
9
10func (l keyList) Len() int { return len(l) }
11func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
12func (l keyList) Less(i, j int) bool {
13 a := l[i]
14 b := l[j]
15 ak := a.Kind()
16 bk := b.Kind()
17 for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
18 a = a.Elem()
19 ak = a.Kind()
20 }
21 for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
22 b = b.Elem()
23 bk = b.Kind()
24 }
25 af, aok := keyFloat(a)
26 bf, bok := keyFloat(b)
27 if aok && bok {
28 if af != bf {
29 return af < bf
30 }
31 if ak != bk {
32 return ak < bk
33 }
34 return numLess(a, b)
35 }
36 if ak != reflect.String || bk != reflect.String {
37 return ak < bk
38 }
39 ar, br := []rune(a.String()), []rune(b.String())
40 for i := 0; i < len(ar) && i < len(br); i++ {
41 if ar[i] == br[i] {
42 continue
43 }
44 al := unicode.IsLetter(ar[i])
45 bl := unicode.IsLetter(br[i])
46 if al && bl {
47 return ar[i] < br[i]
48 }
49 if al || bl {
50 return bl
51 }
52 var ai, bi int
53 var an, bn int64
54 for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
55 an = an*10 + int64(ar[ai]-'0')
56 }
57 for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
58 bn = bn*10 + int64(br[bi]-'0')
59 }
60 if an != bn {
61 return an < bn
62 }
63 if ai != bi {
64 return ai < bi
65 }
66 return ar[i] < br[i]
67 }
68 return len(ar) < len(br)
69}
70
71// keyFloat returns a float value for v if it is a number/bool
72// and whether it is a number/bool or not.
73func keyFloat(v reflect.Value) (f float64, ok bool) {
74 switch v.Kind() {
75 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
76 return float64(v.Int()), true
77 case reflect.Float32, reflect.Float64:
78 return v.Float(), true
79 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
80 return float64(v.Uint()), true
81 case reflect.Bool:
82 if v.Bool() {
83 return 1, true
84 }
85 return 0, true
86 }
87 return 0, false
88}
89
90// numLess returns whether a < b.
91// a and b must necessarily have the same kind.
92func numLess(a, b reflect.Value) bool {
93 switch a.Kind() {
94 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
95 return a.Int() < b.Int()
96 case reflect.Float32, reflect.Float64:
97 return a.Float() < b.Float()
98 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
99 return a.Uint() < b.Uint()
100 case reflect.Bool:
101 return !a.Bool() && b.Bool()
102 }
103 panic("not a number")
104}

Subscribers

People subscribed via source and target branches