Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change NetAddress class to hide its fields behind functions, fixing cross-platform compatibility. #2734

Merged
merged 6 commits into from
Jun 16, 2018

Conversation

aburn
Copy link
Contributor

@aburn aburn commented Jun 1, 2018

made existing fields private. and exposed utility functions to access…… the fields. Platform specific information is handled in these utility functions.

… the fields. Platform specific information is handled in these utility functions.
@aburn aburn changed the title change NetAddress class to handle different OS platforms 2587: change NetAddress class to handle different OS platforms Jun 1, 2018
@aburn aburn changed the title 2587: change NetAddress class to handle different OS platforms issue 2587: change NetAddress class to handle different OS platforms Jun 1, 2018
Copy link
Member

@jemc jemc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this!

I've left some small code style comments.

@@ -1,3 +1,5 @@
type MaybeIPAddr is (U32 | Array[U32])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the one place where this type alias is used doesn't really warrant adding another name to the namespace. I think this line should be removed, and the line below that uses it should just use (U32 | Array[U32]) directly

and (this._addr2 == that._addr2)
and (this._addr3 == that._addr3)
and (this._addr4 == that._addr4)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The end here should be the same level of indentation as the if that it terminates. That is, it should be indented one more level to the right.

fun scope() : U32 =>
_scope

fun addr() : MaybeIPAddr =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method should probably have a docstring to explain exactly what it returns in the (U32 | Array[U32]). That is, it should explain what those 32-bit integers mean in each case.

end

fun _v6_addr() : Array[U32] =>
[_addr1; _addr2; _addr3; _addr4]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This red icon is GitHub's way of saying that this file is missing an empty line at the end, which is a common convention for file endings. Adding an empty line at the end here will make this nag icon go away.

@@ -90,7 +90,7 @@ typedef struct
struct sockaddr_storage addr;
} ipaddress_t;

static socklen_t address_length(ipaddress_t* ipaddr)
PONY_API socklen_t address_length(ipaddress_t* ipaddr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to expose this for FFI use in the standard library, it needs to get either a pony_ or ponyint_ prefix to its name, for cleanliness of the function namespace. pony_ means it is a public API meant for use by third party code, whereas ponyint_ means it is internal. Unless there is a good reason to make it public, we should prefer to keep it internal because public functions require us to treat any changes to them as breaking changes in Pony.

So, this function should end up named ponyint_address_length, or something else with that prefix.

Copy link
Contributor

@mfelsche mfelsche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some inline comments. In general very promising first iteration.

fun scope() : U32 =>
_scope

fun addr() : (U32 | Array[U32]) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about creating two different methods, on for the IPv4 and one for the IPv6 address?
The user has to check what kind of address this is anyways, with a method able to return each, a second match statement necessary, like so:

let addr: NetAddress = ... // somehow get a  NetAddress
if addr.ip4() then
  match addr.addr()
  |  let ip4: U32 => // do something with ip4 address
  else
     // somehow handle this actually impossible case, otherwise this would always return something or None
  end

The as statement is also an alternative here. But after all, it might be more straightforward to do:

let addr: NetAddress = ... // somehow get a  NetAddress
if addr.ip4() then
  let ip4: U32 = addr.ip4_addr()
  // do something with ip4
end

It would need to be documented that the return value of a fun ip4_addr(): U32 method is actually invalid if ip4() is not true. This would basically repeat the behavior we currently have with only the fields.

fun scope() : U32 =>
_scope

fun addr() : (U32 | Array[U32]) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also each returned integer value (except U8 of course) in any of these methods should be run through a method to convert between network and host byte order. These are ntohl for U32 and ntohs for U16.

end

fun family() : U8 =>
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please indent the function body with 2 spaces according to the style guide.

end

fun port() : U16 =>
_port
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use @noths[U16](_port) here, to convert to host byte order.

_port

fun scope() : U32 =>
_scope
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use @nothl[U32](_scope) here, to convert to host byte order.

if ip4() then
_addr
else
[_addr1; _addr2; _addr3; _addr4]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about returning a 4-tuple here instead? Just a thought, Array[U32] is also fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that a 4-tuple is a better idea - the number of elements is a constant known at compile time, so a tuple is a better representation of that. Array[U32] is more cumbersome because your code has to deal with the possibility of the array being more or fewer than 4 elements, which we know is impossible, but the compiler won't know it.

end

fun port() : U16 =>
_port
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use parts of the port field docstring here.

fun port() : U16 =>
_port

fun scope() : U32 =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can move the scope field docstring here.

Bhargava Yammanuru added 2 commits June 2, 2018 17:57
…ad of returning an Array for IPV6 address, made it a 4-tuple to provide more compile time guarantees. Addressed few other review comments.
ifdef linux or windows then
(@ponyint_address_length[U32](this)).u8()
else
ifdef bigendian then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good, i am just not 100% sure about this check. Could we have a testcase that verifies that the methods actually return what they should?

@jemc jemc added changelog - fixed Automatically add "Fixed" CHANGELOG entry on merge changelog - changed Automatically add "Changed" CHANGELOG entry on merge labels Jun 15, 2018
@jemc jemc changed the title issue 2587: change NetAddress class to handle different OS platforms Change NetAddress class to hide its fields behind functions, fixing cross-platform compatibility. Jun 15, 2018
@SeanTAllen
Copy link
Member

I think the change log should be changed rather than fixed

@SeanTAllen SeanTAllen removed the changelog - fixed Automatically add "Fixed" CHANGELOG entry on merge label Jun 16, 2018
@SeanTAllen
Copy link
Member

This is a breaking change, yes? If yes, before this is merged, can someone please supply release notes that show what code used to look like, what sort of error a user should expect and what they need to do to get their code working?

@jemc
Copy link
Member

jemc commented Jun 16, 2018

I think the changelog entry belongs in both sections, which is why I added both tags and updated the PR name to make sense in both contexts.

@SeanTAllen SeanTAllen added the do not merge This PR should not be merged at this time label Jun 16, 2018
@SeanTAllen
Copy link
Member

Marking this as "DO NOT MERGE". Can be removed once release notes are added.

@mfelsche
Copy link
Contributor

Release notes draft

net.NetAddress has been modelled after the C union sockaddr_storage which happens to have different fields on different platforms (i.e. on linux a 16 bit family field and on e.g. OSX an 8 bit length field an only a 8 bit family field). This PR unifies net.NetAddress to return consistent values on any platform Pony is supported on. To this end, all previously public fields on this class have been made private and new functions have been introduced to return those values:

  • Removed Field | New Method
  • length: U8 | length(): U8
  • family: U8 | family(): U8
  • port: U16 | port(): U16
  • addr: U32 | ipv4_addr(): U32
  • addr1, addr2, addr3, addr4 | ipv6_addr(): (U32, U32, U32, U32)
  • scope: U32 | scope(): U32

All functions return the values in host byte order, that means conversion using @ntohX FFI functions is not necessary anymore. Before calling and using ipv4_addr() or ipv6_addr() check ip4(): Bool and ip6(): Bool which one to use, otherwise the returned values will be invalid (as before using the fields).

@mfelsche mfelsche removed the do not merge This PR should not be merged at this time label Jun 16, 2018
@SeanTAllen SeanTAllen merged commit 5d2f5b1 into ponylang:master Jun 16, 2018
ponylang-main added a commit that referenced this pull request Jun 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog - changed Automatically add "Changed" CHANGELOG entry on merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants