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
1=== modified file 'encode.go'
2--- encode.go 2011-12-15 16:20:57 +0000
3+++ encode.go 2012-09-20 09:35:22 +0000
4@@ -5,6 +5,7 @@
5
6 import (
7 "reflect"
8+ "sort"
9 "strconv"
10 "unsafe"
11 )
12@@ -126,7 +127,9 @@
13
14 func (e *encoder) mapv(tag string, in reflect.Value) {
15 e.mappingv(tag, func() {
16- for _, k := range in.MapKeys() {
17+ keys := keyList(in.MapKeys())
18+ sort.Sort(keys)
19+ for _, k := range keys {
20 e.marshal("", k)
21 e.marshal("", in.MapIndex(k))
22 }
23
24=== modified file 'encode_test.go'
25--- encode_test.go 2011-11-24 19:50:00 +0000
26+++ encode_test.go 2012-09-20 09:35:22 +0000
27@@ -1,10 +1,12 @@
28 package goyaml_test
29
30 import (
31+ "fmt"
32 . "launchpad.net/gocheck"
33 "launchpad.net/goyaml"
34 "math"
35- //"reflect"
36+ "strconv"
37+ "strings"
38 )
39
40 var marshalIntTest = 123
41@@ -171,3 +173,68 @@
42 c.Assert(err, IsNil)
43 c.Assert(string(data), Equals, "hello: world!\n")
44 }
45+
46+func (s *S) TestSortedOutput(c *C) {
47+ order := []interface{}{
48+ false,
49+ true,
50+ 1,
51+ uint(1),
52+ 1.0,
53+ 1.1,
54+ 1.2,
55+ 2,
56+ uint(2),
57+ 2.0,
58+ 2.1,
59+ "",
60+ ".1",
61+ ".2",
62+ ".a",
63+ "1",
64+ "2",
65+ "a!10",
66+ "a/2",
67+ "a/10",
68+ "a~10",
69+ "ab/1",
70+ "b/1",
71+ "b/01",
72+ "b/2",
73+ "b/02",
74+ "b/3",
75+ "b/03",
76+ "b1",
77+ "b01",
78+ "b3",
79+ "c2.10",
80+ "c10.2",
81+ "d1",
82+ "d12",
83+ "d12a",
84+ }
85+ m := make(map[interface{}]int)
86+ for _, k := range order {
87+ m[k] = 1
88+ }
89+ data, err := goyaml.Marshal(m)
90+ c.Assert(err, IsNil)
91+ out := "\n" + string(data)
92+ last := 0
93+ for i, k := range order {
94+ repr := fmt.Sprint(k)
95+ if s, ok := k.(string); ok {
96+ if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
97+ repr = `"` + repr + `"`
98+ }
99+ }
100+ index := strings.Index(out, "\n"+repr+":")
101+ if index == -1 {
102+ c.Fatalf("%#v is not in the output: %#v", k, out)
103+ }
104+ if index < last {
105+ c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
106+ }
107+ last = index
108+ }
109+}
110
111=== added file 'sorter.go'
112--- sorter.go 1970-01-01 00:00:00 +0000
113+++ sorter.go 2012-09-20 09:35:22 +0000
114@@ -0,0 +1,104 @@
115+package goyaml
116+
117+import (
118+ "reflect"
119+ "unicode"
120+)
121+
122+type keyList []reflect.Value
123+
124+func (l keyList) Len() int { return len(l) }
125+func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
126+func (l keyList) Less(i, j int) bool {
127+ a := l[i]
128+ b := l[j]
129+ ak := a.Kind()
130+ bk := b.Kind()
131+ for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
132+ a = a.Elem()
133+ ak = a.Kind()
134+ }
135+ for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
136+ b = b.Elem()
137+ bk = b.Kind()
138+ }
139+ af, aok := keyFloat(a)
140+ bf, bok := keyFloat(b)
141+ if aok && bok {
142+ if af != bf {
143+ return af < bf
144+ }
145+ if ak != bk {
146+ return ak < bk
147+ }
148+ return numLess(a, b)
149+ }
150+ if ak != reflect.String || bk != reflect.String {
151+ return ak < bk
152+ }
153+ ar, br := []rune(a.String()), []rune(b.String())
154+ for i := 0; i < len(ar) && i < len(br); i++ {
155+ if ar[i] == br[i] {
156+ continue
157+ }
158+ al := unicode.IsLetter(ar[i])
159+ bl := unicode.IsLetter(br[i])
160+ if al && bl {
161+ return ar[i] < br[i]
162+ }
163+ if al || bl {
164+ return bl
165+ }
166+ var ai, bi int
167+ var an, bn int64
168+ for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
169+ an = an*10 + int64(ar[ai]-'0')
170+ }
171+ for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
172+ bn = bn*10 + int64(br[bi]-'0')
173+ }
174+ if an != bn {
175+ return an < bn
176+ }
177+ if ai != bi {
178+ return ai < bi
179+ }
180+ return ar[i] < br[i]
181+ }
182+ return len(ar) < len(br)
183+}
184+
185+// keyFloat returns a float value for v if it is a number/bool
186+// and whether it is a number/bool or not.
187+func keyFloat(v reflect.Value) (f float64, ok bool) {
188+ switch v.Kind() {
189+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
190+ return float64(v.Int()), true
191+ case reflect.Float32, reflect.Float64:
192+ return v.Float(), true
193+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
194+ return float64(v.Uint()), true
195+ case reflect.Bool:
196+ if v.Bool() {
197+ return 1, true
198+ }
199+ return 0, true
200+ }
201+ return 0, false
202+}
203+
204+// numLess returns whether a < b.
205+// a and b must necessarily have the same kind.
206+func numLess(a, b reflect.Value) bool {
207+ switch a.Kind() {
208+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
209+ return a.Int() < b.Int()
210+ case reflect.Float32, reflect.Float64:
211+ return a.Float() < b.Float()
212+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
213+ return a.Uint() < b.Uint()
214+ case reflect.Bool:
215+ return !a.Bool() && b.Bool()
216+ }
217+ panic("not a number")
218+}

Subscribers

People subscribed via source and target branches