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

group-pm: fix colorHashFromString #3991

Merged
merged 1 commit into from
Apr 13, 2020

Conversation

Maskedman99
Copy link
Contributor

colorHashFromString() now considers all charecters in the string it can also now return colors with red component in it. Also fixed the cases where the function returned #NaN for very long or empty strings.

Fixes: #3985

@Maskedman99
Copy link
Contributor Author

@ray-kraesig since this function is only used in GroupAvatar.js should I move it into that.

Copy link
Member

@jainkuniya jainkuniya left a comment

Choose a reason for hiding this comment

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

hi @Maskedman99 thanks for the PR.

Can you please link before and after screenshots here? It will help a lot in reviewing the PR.

@Maskedman99
Copy link
Contributor Author

Maskedman99 commented Mar 22, 2020

@jainkuniya the difference isn't exactly visible in the UI,

  • apparently colorHashFromString() before would break when dealing with empty strings or strings with just 1 character, but in reality we cannot create a group with no members in it or a username with just 1 character. However this is fixed in the PR by considering every character in the string as opposed to only taking the 2nd character of the string.
  • In case of strings with the length being a certain large value the hash value would become very large and JS returned Infinity, apparently there's no crash in the App, and a color is being returned, but the same color is being returned for every string whose length is greater than this value. This was fixed by doing a modulos operation on hash and assigning the result onto the hash.
  • Before the color was decided based on the second character of the string and it's length, if another string had the same second character and length it would also return the same color, this is fixed in the PR by making use of every charector in the string to determine the color.
  • Before the function couldn't return #ffffff since the final hash was modulosed with #ffffff this is fixed by doing the modulose operator with #1000000 instead.
  • Before the function adds 0x100000 for every hash value which was less than 0x100000, this caused the function to miss out the color range #000000 to #0fffff, this was done to prevent the colorHash value from returning a string with length less than 6, This has been fixed in the PR

Before:

Screenshot_20200322-094923_Zulip (debug)

After:

Screenshot_20200322-094812_Zulip (debug)

for (let i = 0; i < name.length; i++) {
hash = hash * 31 + name.charCodeAt(1);
for (let i = name.length - 1; i >= 0; i--) {
hash = hash * 31 + name.codePointAt(i);
Copy link
Contributor

Choose a reason for hiding this comment

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

codePointAt is normally what you want, for UTF-8 safety – but here charCodeAt is fine. In fact, with this loop, it's required for correctness: name.length gives the number of 16-byte code units, not the number of code points.

Also, why is the string now being iterated in reverse?

Comment on lines 16 to 17
while (colorHash.length < 6) {
colorHash += '0';
Copy link
Contributor

Choose a reason for hiding this comment

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

This allows #000000, but still can't generate any other color with no red component.

Also, you may be interested in the padStart and padEnd functions.

Copy link
Contributor

@rk-for-zulip rk-for-zulip left a comment

Choose a reason for hiding this comment

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

The commit message seems to have been accidentally doubled.


This commit will also need some tests demonstrating that it succeeds where the previous code failed. (See src/utils/__tests__/color-test.js for the existing tests.)

While it's unreasonable to find a string which produces exactly #ffffff (and even less reasonable to require that any possible future implementation also produce #ffffff for that string), it shouldn't be difficult to ensure that its other desirable properties are tested – e.g., that similar strings have different hashes, or that short and long strings generate valid values.

(I'd thought I'd mentioned this in a previous review, but apparently not. Apologies.)

Comment on lines 15 to 18
let colorHash = hash.toString(16);
colorHash = colorHash.padStart(6, '0');

return `#${colorHash}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

This could all be a single line. (The first two lines in particular should be a single line.)

@Maskedman99
Copy link
Contributor Author

since this function is only used in GroupAvatar.js should I move it into that?
Also,

desirable properties are tested – e.g., that similar strings have different hashes,or that short and long strings generate valid values.

you meant the opposite right similar strings have similar hashes

@rk-for-zulip
Copy link
Contributor

since this function is only used in GroupAvatar.js should I move it into that?

While that's the only place it's used now, it's conceptually generic and could reasonably be used elsewhere if there were any call for it. Unless that's going to change, it might as well stay where it is.

(It probably wouldn't be worth the churn to move out, if it were already there. But it's not.)

Also,

desirable properties are tested – e.g., that similar strings have different hashes,or that short and long strings generate valid values.

you meant the opposite right similar strings have similar hashes

Decidedly not! That's often exactly the property you want a hash function not to have; it makes finding collisions easier.

This application doesn't exactly call for a cryptography-grade hash, but it's still desirable that (e.g.) "John Doe, Juan Pérez" and "John Doe, Jean Dupont" have different colors, even though under the current function they wouldn't. Obviously the pigeonhole principle guarantees that this won't be true in the general case, but matches should generally be uncommon.

@Maskedman99 Maskedman99 force-pushed the colorHashFromString branch 2 times, most recently from 4276aa1 to 5f89eb4 Compare March 27, 2020 05:40
@Maskedman99
Copy link
Contributor Author

@ray-kraesig thanks for clarifying, It seems I interpreted 'similar strings' as "John Doe" and
"John Doe". I've added the suggested modifications.

@chrisbobbe
Copy link
Contributor

chrisbobbe commented Apr 13, 2020

Bumping in priority because it's apparent that the bug described in the issue is causing a P0 crash, #4038. I'll try for a simple fix that can ignore the #NaN output, unless @ray-kraesig if you happen to know if this is very close to mergeable (or you can merge with a few tweaks).

@chrisbobbe chrisbobbe added the P0 critical Highest priority label Apr 13, 2020
@rk-for-zulip
Copy link
Contributor

Bumping in priority because it's apparent that the bug described in the issue is causing a P0 crash, #4038. I'll try for a simple fix that can ignore the #NaN output, unless @ray-kraesig if you happen to know if this is very close to mergeable (or you can merge with a few tweaks).

As of the last pass, I think all issues have been resolved. I'll run a couple of additional tests, but it should be mergeable.

colorHashFromString() now considers all characters in the string. It
can also now return colors with no red component in it.

Also fixed the cases where the function returned #NaN for very long or
very short strings.

[ray: Minor copyedits to commit message.]

Fixes: zulip#3985
@rk-for-zulip rk-for-zulip merged commit 9161e47 into zulip:master Apr 13, 2020
@chrisbobbe
Copy link
Contributor

Thanks @Maskedman99 and @ray-kraesig!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P0 critical Highest priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

colorHashFromString is almost nonfunctional
4 participants