Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14036546
D18909.id45362.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D18909.id45362.diff
View Options
diff --git a/src/applications/differential/storage/DifferentialChangeset.php b/src/applications/differential/storage/DifferentialChangeset.php
--- a/src/applications/differential/storage/DifferentialChangeset.php
+++ b/src/applications/differential/storage/DifferentialChangeset.php
@@ -173,7 +173,7 @@
}
public function getAnchorName() {
- return 'change-'.PhabricatorHash::digestForIndex($this->getFilename());
+ return 'change-'.PhabricatorHash::digestForAnchor($this->getFilename());
}
public function getAbsoluteRepositoryPath(
diff --git a/src/infrastructure/util/PhabricatorHash.php b/src/infrastructure/util/PhabricatorHash.php
--- a/src/infrastructure/util/PhabricatorHash.php
+++ b/src/infrastructure/util/PhabricatorHash.php
@@ -3,6 +3,7 @@
final class PhabricatorHash extends Phobject {
const INDEX_DIGEST_LENGTH = 12;
+ const ANCHOR_DIGEST_LENGTH = 12;
/**
* Digest a string using HMAC+SHA1.
@@ -38,8 +39,8 @@
* related hashing (for general purpose hashing, see @{method:digest}).
*
* @param string Input string.
- * @return string 12-byte, case-sensitive alphanumeric hash of the string
- * which
+ * @return string 12-byte, case-sensitive, mostly-alphanumeric hash of
+ * the string.
*/
public static function digestForIndex($string) {
$hash = sha1($string, $raw_output = true);
@@ -63,6 +64,62 @@
return $result;
}
+ /**
+ * Digest a string for use in HTML page anchors. This is similar to
+ * @{method:digestForIndex} but produces purely alphanumeric output.
+ *
+ * This tries to be mostly compatible with the index digest to limit how
+ * much stuff we're breaking by switching to it. For additional discussion,
+ * see T13045.
+ *
+ * @param string Input string.
+ * @return string 12-byte, case-sensitive, purely-alphanumeric hash of
+ * the string.
+ */
+ public static function digestForAnchor($string) {
+ $hash = sha1($string, $raw_output = true);
+
+ static $map;
+ if ($map === null) {
+ $map = '0123456789'.
+ 'abcdefghij'.
+ 'klmnopqrst'.
+ 'uvwxyzABCD'.
+ 'EFGHIJKLMN'.
+ 'OPQRSTUVWX'.
+ 'YZ';
+ }
+
+ $result = '';
+ $accum = 0;
+ $map_size = strlen($map);
+ for ($ii = 0; $ii < self::ANCHOR_DIGEST_LENGTH; $ii++) {
+ $byte = ord($hash[$ii]);
+ $low_bits = ($byte & 0x3F);
+ $accum = ($accum + $byte) % $map_size;
+
+ if ($low_bits < $map_size) {
+ // If an index digest would produce any alphanumeric character, just
+ // use that character. This means that these digests are the same as
+ // digests created with "digestForIndex()" in all positions where the
+ // output character is some character other than "." or "_".
+ $result .= $map[$low_bits];
+ } else {
+ // If an index digest would produce a non-alphumeric character ("." or
+ // "_"), pick an alphanumeric character instead. We accumulate an
+ // index into the alphanumeric character list to try to preserve
+ // entropy here. We could use this strategy for all bytes instead,
+ // but then these digests would differ from digests created with
+ // "digestForIndex()" in all positions, instead of just a small number
+ // of positions.
+ $result .= $map[$accum];
+ }
+ }
+
+ return $result;
+ }
+
+
public static function digestToRange($string, $min, $max) {
if ($min > $max) {
throw new Exception(pht('Maximum must be larger than minimum.'));
diff --git a/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php b/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php
--- a/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php
+++ b/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php
@@ -52,4 +52,47 @@
pht('Distinct characters in hash of: %s', $input));
}
+ public function testHashForAnchor() {
+ $map = array(
+ // For inputs with no "." or "_" in the output, digesting for an index
+ // or an anchor should be the same.
+ 'dog' => array(
+ 'Aliif7Qjd5ct',
+ 'Aliif7Qjd5ct',
+ ),
+ // When an output would contain "." or "_", it should be replaced with
+ // an alphanumeric character in those positions instead.
+ 'fig' => array(
+ 'OpB9tY4i.MOX',
+ 'OpB9tY4imMOX',
+ ),
+ 'cot' => array(
+ '_iF26XU_PsVY',
+ '3iF26XUkPsVY',
+ ),
+ // The replacement characters are well-distributed and generally keep
+ // the entropy of the output high: here, "_" characters in the initial
+ // positions of the digests of "cot" (above) and "dug" (this test) have
+ // different outputs.
+ 'dug' => array(
+ '_XuQnp0LUlUW',
+ '7XuQnp0LUlUW',
+ ),
+ );
+
+ foreach ($map as $input => $expect) {
+ list($expect_index, $expect_anchor) = $expect;
+
+ $this->assertEqual(
+ $expect_index,
+ PhabricatorHash::digestForIndex($input),
+ pht('Index digest of "%s".', $input));
+
+ $this->assertEqual(
+ $expect_anchor,
+ PhabricatorHash::digestForAnchor($input),
+ pht('Anchor digest of "%s".', $input));
+ }
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 11, 10:43 AM (2 d, 19 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6725341
Default Alt Text
D18909.id45362.diff (5 KB)
Attached To
Mode
D18909: Digest changeset anchors into purely alphanumeric strings
Attached
Detach File
Event Timeline
Log In to Comment