-
Notifications
You must be signed in to change notification settings - Fork 39
Conversation
For all the other types, the It seems that we should add the full set of behavior that the other types have, but that probably requires committing to a stable serialization format. |
Yeah, I was also considering updating the |
The format proposed here (comma-separated list of IPRanges, all required to be valid) seems fine to me. None of the other types have a type description wrapper, though, so for consistency we should drop the |
Also, if/when we're ready to move forward with this change, it'll need tests. |
That all sounds good to me. Couple questions for y'all:
|
I'm hardly authoritative here, but here are my opinions.
I'd lightly argue against this for now:
Allowing whitespace is something that should considered holistically for all the types. You can imagine arguing that whitespace around the
Personally, I think a single |
Ah yeah, those are good points about the whitespace. I was going to just lean on the |
For future consideration: |
Added in A tangential thing I encountered when testing is that the |
SGTM
From the code: type IPSet struct {
// rr is the set of IPs that belong to this IPSet. The IPRanges
// are normalized according to IPSetBuilder.normalize, meaning
// they are a sorted, minimal representation (no overlapping
// ranges, no contiguous ranges). The implementation of various
// methods rely on this property.
rr []IPRange
} Any other way of constructing an IPSet (Parse/Unmarshal) will need to normalize |
Right on, that'd explain it. I ran into it when I was manually constructing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First round of feedback. I'll want @bradfitz or @danderson to also weigh in before submitting, since it is new API (albeit parallel to other APIs).
Want to add fuzz support and do a bit of fuzzing? This kind of code is a prime fuzz target.
ipset.go
Outdated
for _, rs := range rss { | ||
r, err = ParseIPRange(rs) | ||
if err != nil { | ||
return &IPSet{}, fmt.Errorf("invalid IP range %q in set %q", rs, s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not repeat the entire input. Instead, let's tell them what's wrong with the input. Something like fmt.Errorf("invalid IP range %q: %w", rs, err)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do. Was mostly hesitant to wrap it because I didn't see wrapping used elsewhere.
ipset.go
Outdated
func (s IPSet) String() string { | ||
var b strings.Builder | ||
for i, r := range s.rr { | ||
b.WriteString(r.String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll allocate less if you use r.AppendTo with a buffer that you re-use across iterations.
var buf []byte
for i, r := range s.rr {
buf = r.AppendTo(buf[:0)
b.Write(buf)
// ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bypassed the use of the strings.Builder
altogether, since it's just using a []byte
internally anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strings.Builder will probably eliminate an allocation, because the internal []byte
can be unsafely re-used as the resulting string. If you're curious, add a benchmark? (It probably doesn't matter in practice.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey neat, you were absolutely right about the extra allocation. Sometimes I forget that stdlib doesn't stray away from unsafe
. Thanks for the insight!
ipset.go
Outdated
} | ||
b.AddRange(r) | ||
} | ||
return b.IPSet() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm somewhat inclined to be defensive and return nil, err
when b.IPSet()
returns a non-nil error. I don't think it's possible now, but maybe that could change. And ISTM that ParseIPSet should be all-or-none. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nil, err
return definitely feels most idiomatic, especially in the case of an all-or-nothing action. I'm struggling to come up with a use case where the resulting empty set would be put to valid use in case of an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went ahead and changed it to a nil, err
return, but am happy to change it back if desired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see that change reflected here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last comments from me. After this, leaving for @bradfitz, since it is adding new (if parallel) API.
ipset.go
Outdated
} | ||
b.AddRange(r) | ||
} | ||
return b.IPSet() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see that change reflected here.
ipset.go
Outdated
|
||
// String returns a string representation of the IPSet. | ||
func (s IPSet) String() string { | ||
var ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do
var b strings.Builder
var buf []byte
Factored variable declarations are fairly rare in practice, so this reads as if you're telling us that b and buf are linked in some important and unusual way. And in this case, spelling out "var" twice takes us from 4 LOC to 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in an err check against the IPSetBuilder.IPSet
call in the ParseIPSet
function in order to be explicit about the return value. Also condensed the var declarations in the String
method. I'd not heard of parens-wrapped var declarations implying an relationship before, but now that you mention it, it makes total sense. Plus it feels easier to read this way somehow.
Oh, and thanks for doing this! |
Hey no problem at all. It's always nice to collaborate with other folks and learn how they write code. I appreciate the feedback y'all have provided so far!
Also, happy to do this in the near future. Been looking for a reason to mess around with |
Ah, didn't read the |
I forgot about that too. :) |
Addresses: #193 Signed-off-by: Matt Schultz <[email protected]>
Addresses: #193