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

Update RegionInfo data and Fix RegionInfo.CurrentRegion on Windows #33834

Merged

Conversation

tarekgh
Copy link
Member

@tarekgh tarekgh commented Mar 19, 2020

Fixes #32753

Currently when running on Windows, the call of RegionInfo.CurrentRegion will return the region object matching CurrentCulture. Windows has different settings for the default region than the current culture. The good example is mentioned in the scenario in the attached issue which having current culture was set to English (Europ) and Country was set to Netherland and when getting CurrentRegion we return 159 because Europ doesn't have country code (because it is not a country). We suppose to return the region for Netherland.
The change here is fixing this issue and updating the region data table too to reflect latest list of regions we have in the world.

@Dotnet-GitSync-Bot
Copy link
Collaborator

I couldn't add an area label to this PR.

Checkout this page to find out which area owner to ping, or please add exactly one area label to help train me in the future.

@tarekgh
Copy link
Member Author

tarekgh commented Mar 20, 2020

I logged the issue #33875 to track Mono's failures in the CI.
I logged the issue #33876 for the other CI restoring coverlet (dup of #33837)

@tarekgh
Copy link
Member Author

tarekgh commented Mar 20, 2020

@jkotas @stephentoub please let me know if you have any more feedback or we ok to merge. the failures in the CI are unrelated and tracked with other linked issues.

int geoIsoIdLength = Interop.Kernel32.GetGeoInfo(geoId, Interop.Kernel32.GEO_ISO2, ref MemoryMarshal.GetReference(geoIso2Letters), geoIso2Letters.Length, Interop.Kernel32.EN_LANG_ID);
if (geoIsoIdLength != 0)
{
geoIsoIdLength -= geoIso2Letters[geoIsoIdLength - 1] == 0 ? 1 : 0; // handle null termination and exclude it.
Copy link
Member

Choose a reason for hiding this comment

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

So sometimes it's null terminated and sometimes not? Why does it not return ERROR_INSUFFICIENT_BUFFER when it has no room to null terminate?

Copy link
Member Author

@tarekgh tarekgh Mar 20, 2020

Choose a reason for hiding this comment

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

The doc is not ensuring returning null that is why I have to check to know if we got the null or not.
We are requesting the 2-letters country code and I am providing a buffer of length 10 which is far enough to ensure not running into the ERROR_INSUFFICIENT_BUFFER case even with null.

Copy link
Member

@jkotas jkotas Mar 20, 2020

Choose a reason for hiding this comment

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

From what I can tell, this is always returning zero terminated string.

Would it be best to create the string from the pointer and not bother with using the returned size? I would be fine with not using the Span in that case because of there is no buffer math that can go wrong.

char* pGeoIso2Letters = stackalloc char[10];

if (Interop.Kernel32.GetGeoInfo(..., pGeoIso2Letters, ...) != 0)
{
    CultureData? cd = GetCultureDataForRegion(new string(pGeoIso2Letters), ...

Copy link
Member Author

Choose a reason for hiding this comment

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

I would be more conservative checking null as the doc didn't ensure that and I am not sure what happen if we run on a different version of Windows.

@@ -152,135 +152,262 @@ internal partial class CultureData
/// </remarks>
private static Dictionary<string, string> RegionNames =>
s_regionNames ??=
new Dictionary<string, string>(211 /* prime */)
new Dictionary<string, string>(257 /* prime */)
Copy link
Member

Choose a reason for hiding this comment

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

nit, I don't think there is any value in passing in a prime number - the dictionary internally rounds up to the next prime number from its list. it's harmless though of course.

Ideally the C# compiler would know that there's N entries coming in the initializer and pass in N to the constructor.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll keep it as it is harmless and I am not sure what the C# initializer how it behaves there.

Copy link
Member

Choose a reason for hiding this comment

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

fine, fyi the initializer always uses the parameterless constructor, so it is a valid microoptmization to pass a size here.

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

Thanks

@tarekgh tarekgh merged commit 3a769f7 into dotnet:master Mar 21, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 10, 2020
@tarekgh tarekgh deleted the UpdateRegionDataAndCurrentRegionForWindows branch March 15, 2021 19:41
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Region.TwoLetterISORegionName can return three numerical digits
5 participants