{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/corefx/issues/4518","labels_url":"https://api.github.com/repos/dotnet/corefx/issues/4518/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/corefx/issues/4518/comments","events_url":"https://api.github.com/repos/dotnet/corefx/issues/4518/events","html_url":"https://github.com/dotnet/corefx/pull/4518","id":116951896,"number":4518,"title":"Reduce the size of System.Net.IPAddress","user":{"login":"justinvp","id":710598,"avatar_url":"https://avatars.githubusercontent.com/u/710598?v=3","gravatar_id":"","url":"https://api.github.com/users/justinvp","html_url":"https://github.com/justinvp","followers_url":"https://api.github.com/users/justinvp/followers","following_url":"https://api.github.com/users/justinvp/following{/other_user}","gists_url":"https://api.github.com/users/justinvp/gists{/gist_id}","starred_url":"https://api.github.com/users/justinvp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/justinvp/subscriptions","organizations_url":"https://api.github.com/users/justinvp/orgs","repos_url":"https://api.github.com/users/justinvp/repos","events_url":"https://api.github.com/users/justinvp/events{/privacy}","received_events_url":"https://api.github.com/users/justinvp/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/dotnet/corefx/labels/cla-already-signed","name":"cla-already-signed","color":"009800"},{"url":"https://api.github.com/repos/dotnet/corefx/labels/Needs%20extra%20testing%20before%20merge","name":"Needs extra testing before merge","color":"e11d21"},{"url":"https://api.github.com/repos/dotnet/corefx/labels/System.Net","name":"System.Net","color":"d4c5f9"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/dotnet/corefx/milestones/6","html_url":"https://github.com/dotnet/corefx/milestones/1.0.0-rc2","labels_url":"https://api.github.com/repos/dotnet/corefx/milestones/6/labels","id":1365455,"number":6,"title":"1.0.0-rc2","description":"","creator":{"login":"joshfree","id":1302850,"avatar_url":"https://avatars.githubusercontent.com/u/1302850?v=3","gravatar_id":"","url":"https://api.github.com/users/joshfree","html_url":"https://github.com/joshfree","followers_url":"https://api.github.com/users/joshfree/followers","following_url":"https://api.github.com/users/joshfree/following{/other_user}","gists_url":"https://api.github.com/users/joshfree/gists{/gist_id}","starred_url":"https://api.github.com/users/joshfree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joshfree/subscriptions","organizations_url":"https://api.github.com/users/joshfree/orgs","repos_url":"https://api.github.com/users/joshfree/repos","events_url":"https://api.github.com/users/joshfree/events{/privacy}","received_events_url":"https://api.github.com/users/joshfree/received_events","type":"User","site_admin":false},"open_issues":148,"closed_issues":79,"state":"open","created_at":"2015-10-20T04:29:15Z","updated_at":"2015-11-16T21:01:45Z","due_on":"2016-02-03T08:00:00Z","closed_at":null},"comments":4,"created_at":"2015-11-14T21:01:56Z","updated_at":"2015-11-16T21:24:51Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/dotnet/corefx/pulls/4518","html_url":"https://github.com/dotnet/corefx/pull/4518","diff_url":"https://github.com/dotnet/corefx/pull/4518.diff","patch_url":"https://github.com/dotnet/corefx/pull/4518.patch"},"body":"This change reduces the size of `IPAddress` from 40 bytes to 24 bytes (64 bit) and also avoids an unnecessary array allocation for IPv4 addresses.\r\n\r\ncc: @davidsh, @CIPop, @stephentoub\r\n\r\nThese are the current fields on `IPAddress` that add up to 40 bytes (64 bit):\r\n\r\n```c#\r\ninternal long Address;\r\ninternal string StringRepresentation;\r\nprivate readonly AddressFamily _family = AddressFamily.InterNetwork;\r\nprivate readonly ushort[] _numbers = new ushort[NumberOfLabels];\r\nprivate long _scopeId = 0;\r\nprivate int _hashCode = 0;\r\n```\r\n\r\n - `Address` is only used for IPv4 addresses. The size is actually `uint`, but it is typed as `long` because public APIs use `long` for CLS compliance. This field can be changed to `uint` to save 4 bytes.\r\n\r\n - `StringRepresentation` is a cache of the result of `ToString()` and can remain as-is.\r\n\r\n - `_family` indicates whether the `IPAddress` instance is an IPv4 address or an IPv6 address through the `AddressFamily.InterNetwork` (IPv4) and `AddressFamily.InterNetworkV6` (IPv6) enum values. We can eliminate this field and instead use the `_numbers` field, which is only used for IPv6 addresses, to indicate whether or not the address is IPv4 vs. IPv6. This saves 4 bytes.\r\n\r\n - `_numbers`, as mentioned in the previous bullet, is only used for IPv6 addresses. Note that currently the array is always being allocated, even for IPv4 addresses. We can change this to only allocate the array for IPv6 addresses. When the field is null, it indicates that the address is an IPv4 address, when the field is non-null it is an IPv6 address.\r\n\r\n - `_scopeId` is only used for IPv6 addresses. Similar to `Address`, the size is actually `uint`, but it is typed as `long` because public APIs use `long` for CLS compliance. We can eliminate this field and instead use the same `uint` field as `Address` for IPv4 addresses, reducing the size by another 8 bytes.\r\n\r\n - `_hashCode` is only used for IPv6 addresses (IPv4 just uses the `Address` for the hash code). We could consider getting rid of this field, to reduce the size by another 4 bytes, and instead always re-calculate the hash code when `GetHashCode` is called (all it's doing is calling `StringComparer.OrdinalIgnoreCase.GetHashCode()` on a cached `ToString()` result), but I assume it is here for performance reasons, so I've kept it for now.\r\n\r\nHere are the fields after the changes:\r\n\r\n```C#\r\nprivate uint _addressOrScopeId;\r\nprivate readonly ushort[] _numbers;\r\nprivate string _toString;\r\nprivate int _hashCode;\r\n```\r\n\r\nFrom 40 bytes to 24 bytes (64 bit).\r\n\r\n(Note: I renamed some of the fields and made them all private. The two renamed internal fields (`Address` and `StringRepresentation`) now have internal properties with the same type and name as the old fields, to avoid breaking any internal callers.)\r\n\r\n(There are other potential optimizations for `IPAddress` that could be handled in separate PRs. For example, `ToString()` could be optimized to avoid unnecessary intermediate allocations. Also, as noted in #2891, some means to access to the internal buffer to avoid unnecessary `byte[]` allocations could be added.)"},"comment":{"url":"https://api.github.com/repos/dotnet/corefx/issues/comments/157175322","html_url":"https://github.com/dotnet/corefx/pull/4518#issuecomment-157175322","issue_url":"https://api.github.com/repos/dotnet/corefx/issues/4518","id":157175322,"user":{"login":"sharwell","id":1408396,"avatar_url":"https://avatars.githubusercontent.com/u/1408396?v=3","gravatar_id":"","url":"https://api.github.com/users/sharwell","html_url":"https://github.com/sharwell","followers_url":"https://api.github.com/users/sharwell/followers","following_url":"https://api.github.com/users/sharwell/following{/other_user}","gists_url":"https://api.github.com/users/sharwell/gists{/gist_id}","starred_url":"https://api.github.com/users/sharwell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sharwell/subscriptions","organizations_url":"https://api.github.com/users/sharwell/orgs","repos_url":"https://api.github.com/users/sharwell/repos","events_url":"https://api.github.com/users/sharwell/events{/privacy}","received_events_url":"https://api.github.com/users/sharwell/received_events","type":"User","site_admin":false},"created_at":"2015-11-16T21:24:51Z","updated_at":"2015-11-16T21:24:51Z","body":":bulb: I would prefer to see all accesses to `_addressOrScopeId` go through either `Address` or `ScopeId`, and that each of those properties contain an assertion (`Debug.Assert`?) that it is used on an address of the correct form. The performance in release builds would be comparable, but the code would be much easier to understand regarding the intent of each access."}}