Skip to content

Commit 82864b6

Browse files
committed
support negative object/feature/element ids
1 parent 02986e0 commit 82864b6

18 files changed

+187
-23
lines changed

changeset.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type ChangesetID int64
1010

1111
// ObjectID is a helper returning the object id for this changeset id.
1212
func (id ChangesetID) ObjectID() ObjectID {
13-
return ObjectID(changesetMask | (id << versionBits))
13+
return ObjectID(changesetMask | ((id << versionBits) & refVersionMask))
1414
}
1515

1616
// Changesets is a collection with some helper functions attached.

changeset_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ func TestChangeset_MarshalXML(t *testing.T) {
262262
}
263263

264264
func TestChangesets_IDs(t *testing.T) {
265-
cs := Changesets{{ID: 1}, {ID: 2}}
265+
cs := Changesets{{ID: 1}, {ID: 2}, {ID: -3}}
266266

267-
csids := []ChangesetID{1, 2}
267+
csids := []ChangesetID{1, 2, -3}
268268
if ids := cs.IDs(); !reflect.DeepEqual(ids, csids) {
269269
t.Errorf("incorrect changeset id: %v", csids)
270270
}

element.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ func (id ElementID) Type() Type {
3434

3535
// Ref return the ID reference for the element. Not unique without the type.
3636
func (id ElementID) Ref() int64 {
37-
return int64((id & refMask) >> versionBits)
37+
// handle negative ids correctly, if negative top 24 bits need to be set as 1
38+
// want to do this in a non-branching way
39+
// - shift left until 25th bit is now at first position
40+
// - shift right back to original position,
41+
// this option fill with same as the first position
42+
return (int64((id&refMask)>>versionBits) << typeVersionBits) >> typeVersionBits
3843
}
3944

4045
// Version returns the version of the element.
@@ -180,7 +185,14 @@ type elementsSort Elements
180185
func (es elementsSort) Len() int { return len(es) }
181186
func (es elementsSort) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
182187
func (es elementsSort) Less(i, j int) bool {
183-
return es[i].ElementID() < es[j].ElementID()
188+
iid := es[i].ElementID()
189+
jid := es[j].ElementID()
190+
191+
if iid&typeMask != jid&typeMask {
192+
return iid&typeMask < jid&typeMask
193+
}
194+
195+
return ((iid << typeVersionBits) >> typeVersionBits) < ((jid << typeVersionBits) >> typeVersionBits)
184196
}
185197

186198
// ElementIDs is a list of element ids with helper functions on top.
@@ -213,5 +225,8 @@ func (ids ElementIDs) Sort() {
213225
func (ids elementIDsSort) Len() int { return len(ids) }
214226
func (ids elementIDsSort) Swap(i, j int) { ids[i], ids[j] = ids[j], ids[i] }
215227
func (ids elementIDsSort) Less(i, j int) bool {
216-
return ids[i] < ids[j]
228+
if ids[i]&typeMask != ids[j]&typeMask {
229+
return ids[i]&typeMask < ids[j]&typeMask
230+
}
231+
return ((ids[i] << typeVersionBits) >> typeVersionBits) < ((ids[j] << typeVersionBits) >> typeVersionBits)
217232
}

element_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,26 @@ func TestElementID_ids(t *testing.T) {
2727
t.Errorf("incorrect id: %v", v)
2828
}
2929

30+
if v := NodeID(-1).ElementID(1).NodeID(); v != -1 {
31+
t.Errorf("incorrect id: %v", v)
32+
}
33+
3034
if v := NodeID(1).ElementID(1).NodeID(); v != 1 {
3135
t.Errorf("incorrect id: %v", v)
3236
}
3337

38+
if v := WayID(-1).ElementID(1).WayID(); v != -1 {
39+
t.Errorf("incorrect id: %v", v)
40+
}
41+
3442
if v := WayID(1).ElementID(1).WayID(); v != 1 {
3543
t.Errorf("incorrect id: %v", v)
3644
}
3745

46+
if v := RelationID(-1).ElementID(1).RelationID(); v != -1 {
47+
t.Errorf("incorrect id: %v", v)
48+
}
49+
3850
if v := RelationID(1).ElementID(1).RelationID(); v != 1 {
3951
t.Errorf("incorrect id: %v", v)
4052
}
@@ -79,6 +91,10 @@ func TestParseElementID(t *testing.T) {
7991
string string
8092
id ElementID
8193
}{
94+
{
95+
name: "negative id",
96+
id: NodeID(-1).ElementID(1),
97+
},
8298
{
8399
name: "node",
84100
id: NodeID(0).ElementID(1),
@@ -232,6 +248,7 @@ func TestElements_Sort(t *testing.T) {
232248
es := Elements{
233249
&Node{ID: 1, Version: 4},
234250
&Node{ID: 1, Version: 5},
251+
&Node{ID: -1, Version: 5},
235252
&Way{ID: 2, Version: 6},
236253
&Relation{ID: 3, Version: 7},
237254
&Way{ID: 2, Version: 5},
@@ -240,6 +257,7 @@ func TestElements_Sort(t *testing.T) {
240257
es.Sort()
241258

242259
expected := ElementIDs{
260+
NodeID(-1).ElementID(5),
243261
NodeID(1).ElementID(4),
244262
NodeID(1).ElementID(5),
245263
NodeID(4).ElementID(8),
@@ -279,12 +297,16 @@ func TestElementIDs_Sort(t *testing.T) {
279297
ids := ElementIDs{
280298
RelationID(1).ElementID(1),
281299
NodeID(1).ElementID(2),
300+
NodeID(-1).ElementID(3),
301+
NodeID(-1).ElementID(2),
282302
WayID(2).ElementID(3),
283303
WayID(1).ElementID(2),
284304
WayID(1).ElementID(1),
285305
}
286306

287307
expected := ElementIDs{
308+
NodeID(-1).ElementID(2),
309+
NodeID(-1).ElementID(3),
288310
NodeID(1).ElementID(2),
289311
WayID(1).ElementID(1),
290312
WayID(1).ElementID(2),

feature.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,18 @@ const (
6767
featureMask = 0x7FFFFFFFFFFF0000
6868
typeMask = 0x7F00000000000000
6969

70+
refVersionMask = refMask | versionMask
71+
72+
typeBits = 8
7073
boundsMask = 0x0800000000000000
7174
nodeMask = 0x1000000000000000
7275
wayMask = 0x2000000000000000
7376
relationMask = 0x3000000000000000
7477
changesetMask = 0x4000000000000000
7578
noteMask = 0x5000000000000000
7679
userMask = 0x6000000000000000
80+
81+
typeVersionBits = typeBits + versionBits
7782
)
7883

7984
// A FeatureID is an identifier for a feature in OSM.
@@ -97,7 +102,12 @@ func (id FeatureID) Type() Type {
97102

98103
// Ref return the ID reference for the feature. Not unique without the type.
99104
func (id FeatureID) Ref() int64 {
100-
return int64((id & refMask) >> versionBits)
105+
// handle negative ids correctly, if negative top 24 bits need to be set as 1
106+
// want to do this in a non-branching way
107+
// - shift left until 25th bit is now at first position
108+
// - shift right back to original position,
109+
// this option fill with same as the first position
110+
return (int64((id&refMask)>>versionBits) << typeVersionBits) >> typeVersionBits
101111
}
102112

103113
// ObjectID is a helper to convert the id to an object id.
@@ -205,5 +215,8 @@ func (ids FeatureIDs) Sort() {
205215
func (ids featureIDsSort) Len() int { return len(ids) }
206216
func (ids featureIDsSort) Swap(i, j int) { ids[i], ids[j] = ids[j], ids[i] }
207217
func (ids featureIDsSort) Less(i, j int) bool {
208-
return ids[i] < ids[j]
218+
if ids[i]&typeMask != ids[j]&typeMask {
219+
return ids[i]&typeMask < ids[j]&typeMask
220+
}
221+
return ids[i].Ref() < ids[j].Ref()
209222
}

feature_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,26 @@ func TestFeatureID_ids(t *testing.T) {
3535
t.Errorf("incorrect version: %v", v)
3636
}
3737

38+
if v := NodeID(-1).FeatureID().NodeID(); v != -1 {
39+
t.Errorf("incorrect id: %v", v)
40+
}
41+
3842
if v := NodeID(1).FeatureID().NodeID(); v != 1 {
3943
t.Errorf("incorrect id: %v", v)
4044
}
4145

46+
if v := WayID(-1).FeatureID().WayID(); v != -1 {
47+
t.Errorf("incorrect id: %v", v)
48+
}
49+
4250
if v := WayID(1).FeatureID().WayID(); v != 1 {
4351
t.Errorf("incorrect id: %v", v)
4452
}
4553

54+
if v := RelationID(-1).FeatureID().RelationID(); v != -1 {
55+
t.Errorf("incorrect id: %v", v)
56+
}
57+
4658
if v := RelationID(1).FeatureID().RelationID(); v != 1 {
4759
t.Errorf("incorrect id: %v", v)
4860
}
@@ -94,6 +106,11 @@ func TestFeature_String(t *testing.T) {
94106
id FeatureID
95107
expected string
96108
}{
109+
{
110+
name: "node",
111+
id: NodeID(-1).FeatureID(),
112+
expected: "node/-1",
113+
},
97114
{
98115
name: "node",
99116
id: NodeID(1).FeatureID(),
@@ -200,12 +217,14 @@ func TestFeatureIDs_Counts(t *testing.T) {
200217
func TestFeatureIDs_Sort(t *testing.T) {
201218
ids := FeatureIDs{
202219
RelationID(1).FeatureID(),
220+
NodeID(-1).FeatureID(),
203221
NodeID(1).FeatureID(),
204222
WayID(2).FeatureID(),
205223
WayID(1).FeatureID(),
206224
}
207225

208226
expected := FeatureIDs{
227+
NodeID(-1).FeatureID(),
209228
NodeID(1).FeatureID(),
210229
WayID(1).FeatureID(),
211230
WayID(2).FeatureID(),

node.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (id NodeID) ObjectID(v int) ObjectID {
1818

1919
// FeatureID is a helper returning the feature id for this node id.
2020
func (id NodeID) FeatureID() FeatureID {
21-
return FeatureID(nodeMask | (id << versionBits))
21+
return FeatureID(nodeMask | ((id << versionBits) & refVersionMask))
2222
}
2323

2424
// ElementID is a helper to convert the id to an element id.

node_test.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,19 +90,28 @@ func TestNodes_ids(t *testing.T) {
9090
ns := Nodes{
9191
{ID: 1, Version: 3},
9292
{ID: 2, Version: 4},
93+
{ID: -3, Version: 5},
9394
}
9495

95-
eids := ElementIDs{NodeID(1).ElementID(3), NodeID(2).ElementID(4)}
96+
eids := ElementIDs{
97+
NodeID(1).ElementID(3),
98+
NodeID(2).ElementID(4),
99+
NodeID(-3).ElementID(5),
100+
}
96101
if ids := ns.ElementIDs(); !reflect.DeepEqual(ids, eids) {
97102
t.Errorf("incorrect element ids: %v", ids)
98103
}
99104

100-
fids := FeatureIDs{NodeID(1).FeatureID(), NodeID(2).FeatureID()}
105+
fids := FeatureIDs{
106+
NodeID(1).FeatureID(),
107+
NodeID(2).FeatureID(),
108+
NodeID(-3).FeatureID(),
109+
}
101110
if ids := ns.FeatureIDs(); !reflect.DeepEqual(ids, fids) {
102111
t.Errorf("incorrect feature ids: %v", ids)
103112
}
104113

105-
nids := []NodeID{1, 2}
114+
nids := []NodeID{1, 2, -3}
106115
if ids := ns.IDs(); !reflect.DeepEqual(ids, nids) {
107116
t.Errorf("incorrect node ids: %v", nids)
108117
}
@@ -113,7 +122,7 @@ func TestNodes_SortByIDVersion(t *testing.T) {
113122
{ID: 7, Version: 3},
114123
{ID: 2, Version: 4},
115124
{ID: 5, Version: 2},
116-
{ID: 5, Version: 3},
125+
{ID: -1, Version: 3},
117126
{ID: 5, Version: 4},
118127
{ID: 3, Version: 4},
119128
{ID: 4, Version: 4},
@@ -123,11 +132,11 @@ func TestNodes_SortByIDVersion(t *testing.T) {
123132
ns.SortByIDVersion()
124133

125134
eids := ElementIDs{
135+
NodeID(-1).ElementID(3),
126136
NodeID(2).ElementID(4),
127137
NodeID(3).ElementID(4),
128138
NodeID(4).ElementID(4),
129139
NodeID(5).ElementID(2),
130-
NodeID(5).ElementID(3),
131140
NodeID(5).ElementID(4),
132141
NodeID(7).ElementID(3),
133142
NodeID(9).ElementID(4),

note.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type NoteID int64
1010

1111
// ObjectID is a helper returning the object id for this note id.
1212
func (id NoteID) ObjectID() ObjectID {
13-
return ObjectID(noteMask | (id << versionBits))
13+
return ObjectID(noteMask | ((id << versionBits) & refVersionMask))
1414
}
1515

1616
const dateLayout = "2006-01-02 15:04:05 MST"

note_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,18 @@ func TestNote_ObjectID(t *testing.T) {
167167
}
168168

169169
if v := id.Ref(); v != 123 {
170-
t.Errorf("incorrect ref: %v", 123)
170+
t.Errorf("incorrect ref: %v", v)
171+
}
172+
173+
// negative id
174+
n = Note{ID: -123}
175+
id = n.ObjectID()
176+
177+
if v := id.Type(); v != TypeNote {
178+
t.Errorf("incorrect type: %v", v)
179+
}
180+
181+
if v := id.Ref(); v != -123 {
182+
t.Errorf("incorrect ref: %v", v)
171183
}
172184
}

0 commit comments

Comments
 (0)