FNextPushId.m 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FirebaseDatabase/Sources/Utilities/FNextPushId.h"
  17. #import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
  18. static NSString *const PUSH_CHARS =
  19. @"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
  20. static NSString *const MIN_PUSH_CHAR = @"-";
  21. static NSString *const MAX_PUSH_CHAR = @"z";
  22. static NSInteger const MAX_KEY_LEN = 786;
  23. @implementation FNextPushId
  24. + (NSString *)get:(NSTimeInterval)currentTime {
  25. static long long lastPushTime = 0;
  26. static int lastRandChars[12];
  27. long long now = (long long)(currentTime * 1000);
  28. BOOL duplicateTime = now == lastPushTime;
  29. lastPushTime = now;
  30. unichar timeStampChars[8];
  31. for (int i = 7; i >= 0; i--) {
  32. timeStampChars[i] = [PUSH_CHARS characterAtIndex:(now % 64)];
  33. now = (long long)floor(now / 64);
  34. }
  35. NSMutableString *id = [[NSMutableString alloc] init];
  36. [id appendString:[NSString stringWithCharacters:timeStampChars length:8]];
  37. if (!duplicateTime) {
  38. for (int i = 0; i < 12; i++) {
  39. lastRandChars[i] = (int)floor(arc4random() % 64);
  40. }
  41. } else {
  42. int i = 0;
  43. for (i = 11; i >= 0 && lastRandChars[i] == 63; i--) {
  44. lastRandChars[i] = 0;
  45. }
  46. lastRandChars[i]++;
  47. }
  48. for (int i = 0; i < 12; i++) {
  49. [id appendFormat:@"%C", [PUSH_CHARS characterAtIndex:lastRandChars[i]]];
  50. }
  51. return [NSString stringWithString:id];
  52. }
  53. + (NSString *)successor:(NSString *_Nonnull)key {
  54. NSInteger keyAsInt;
  55. if ([FUtilities tryParseString:key asInt:&keyAsInt]) {
  56. if (keyAsInt == [FUtilities int32max]) {
  57. return MIN_PUSH_CHAR;
  58. }
  59. return [NSString stringWithFormat:@"%ld", (long)keyAsInt + 1];
  60. }
  61. NSMutableString *next = [NSMutableString stringWithString:key];
  62. if ([next length] < MAX_KEY_LEN) {
  63. [next insertString:MIN_PUSH_CHAR atIndex:[key length]];
  64. return next;
  65. }
  66. long i = [next length] - 1;
  67. while (i >= 0) {
  68. if ([next characterAtIndex:i] != [MAX_PUSH_CHAR characterAtIndex:0]) {
  69. break;
  70. }
  71. --i;
  72. }
  73. // `nextAfter` was called on the largest possible key, so return the
  74. // maxName, which sorts larger than all keys.
  75. if (i == -1) {
  76. return [FUtilities maxName];
  77. }
  78. NSString *source =
  79. [NSString stringWithFormat:@"%C", [next characterAtIndex:i]];
  80. NSInteger sourceIndex = [PUSH_CHARS rangeOfString:source].location;
  81. NSString *sourcePlusOne = [NSString
  82. stringWithFormat:@"%C", [PUSH_CHARS characterAtIndex:sourceIndex + 1]];
  83. [next replaceCharactersInRange:NSMakeRange(i, i + 1)
  84. withString:sourcePlusOne];
  85. return [next substringWithRange:NSMakeRange(0, i + 1)];
  86. }
  87. // `key` is assumed to be non-empty.
  88. + (NSString *)predecessor:(NSString *_Nonnull)key {
  89. NSInteger keyAsInt;
  90. if ([FUtilities tryParseString:key asInt:&keyAsInt]) {
  91. if (keyAsInt == [FUtilities int32min]) {
  92. return [FUtilities minName];
  93. }
  94. return [NSString stringWithFormat:@"%ld", (long)keyAsInt - 1];
  95. }
  96. NSMutableString *next = [NSMutableString stringWithString:key];
  97. if ([next characterAtIndex:(next.length - 1)] ==
  98. [MIN_PUSH_CHAR characterAtIndex:0]) {
  99. if ([next length] == 1) {
  100. return
  101. [NSString stringWithFormat:@"%ld", (long)[FUtilities int32max]];
  102. }
  103. // If the last character is the smallest possible character, then the
  104. // next smallest string is the prefix of `key` without it.
  105. [next replaceCharactersInRange:NSMakeRange([next length] - 1, 1)
  106. withString:@""];
  107. return next;
  108. }
  109. // Replace the last character with its immedate predecessor, and fill the
  110. // suffix of the key with MAX_PUSH_CHAR. This is the lexicographically
  111. // largest possible key smaller than `key`.
  112. unichar curr = [next characterAtIndex:next.length - 1];
  113. NSRange dstRange = NSMakeRange([next length] - 1, 1);
  114. NSRange srcRange =
  115. [PUSH_CHARS rangeOfString:[NSString stringWithFormat:@"%C", curr]];
  116. srcRange.location -= 1;
  117. [next replaceCharactersInRange:dstRange
  118. withString:[PUSH_CHARS substringWithRange:srcRange]];
  119. return [next stringByPaddingToLength:MAX_KEY_LEN
  120. withString:MAX_PUSH_CHAR
  121. startingAtIndex:0];
  122. };
  123. @end