Skip to content

Commit d49bb11

Browse files
DaniilSokolyukdustinsoftware
authored andcommitted
Fast React ID Generator (#528)
* Fast ReactID Generator * review fixes * fix comment
1 parent 943baec commit d49bb11

14 files changed

+211
-101
lines changed

src/React.Core/AssemblyRegistration.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* Copyright (c) 2014-Present, Facebook, Inc.
33
* All rights reserved.
44
*
@@ -37,6 +37,7 @@ public void Register(TinyIoCContainer container)
3737
container.Register<IFileCacheHash, FileCacheHash>().AsPerRequestSingleton();
3838
container.Register<JsEngineSwitcher>((c, o) => JsEngineSwitcher.Instance);
3939
container.Register<IJavaScriptEngineFactory, JavaScriptEngineFactory>().AsSingleton();
40+
container.Register<IReactIdGenerator, ReactIdGenerator>().AsSingleton();
4041

4142
container.Register<IReactEnvironment, ReactEnvironment>().AsPerRequestSingleton();
4243
}

src/React.Core/GuidExtensions.cs

-32
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* Copyright (c) 2016-Present, Facebook, Inc.
33
* All rights reserved.
44
*
@@ -7,18 +7,14 @@
77
* of patent rights can be found in the PATENTS file in the same directory.
88
*/
99

10-
using System;
11-
using Xunit;
12-
13-
namespace React.Tests.Core
10+
namespace React
1411
{
15-
public class GuidExtensionsTests
12+
public interface IReactIdGenerator
1613
{
17-
[Fact]
18-
public void ToShortGuid()
19-
{
20-
var guid = Guid.Parse("c027191d-3785-485d-9fd7-5e0b376bd547");
21-
Assert.Equal("HRknwIU3XUif114LN2vVRw", guid.ToShortGuid());
22-
}
14+
/// <summary>
15+
/// Returns a short react identifier starts with "react_".
16+
/// </summary>
17+
/// <returns></returns>
18+
string Generate();
2319
}
2420
}

src/React.Core/ReactComponent.cs

+3-11
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,16 @@ public object Props
9797
/// </summary>
9898
/// <param name="environment">The environment.</param>
9999
/// <param name="configuration">Site-wide configuration.</param>
100+
/// <param name="reactIdGenerator">React Id generator.</param>
100101
/// <param name="componentName">Name of the component.</param>
101102
/// <param name="containerId">The ID of the container DIV for this component</param>
102-
public ReactComponent(IReactEnvironment environment, IReactSiteConfiguration configuration, string componentName, string containerId)
103+
public ReactComponent(IReactEnvironment environment, IReactSiteConfiguration configuration, IReactIdGenerator reactIdGenerator, string componentName, string containerId)
103104
{
104105
EnsureComponentNameValid(componentName);
105106
_environment = environment;
106107
_configuration = configuration;
107108
ComponentName = componentName;
108-
ContainerId = string.IsNullOrEmpty(containerId) ? GenerateId() : containerId;
109+
ContainerId = string.IsNullOrEmpty(containerId) ? reactIdGenerator.Generate() : containerId;
109110
ContainerTag = "div";
110111
}
111112

@@ -229,14 +230,5 @@ internal static void EnsureComponentNameValid(string componentName)
229230
throw new ReactInvalidComponentException($"Invalid component name '{componentName}'");
230231
}
231232
}
232-
233-
/// <summary>
234-
/// Generates a unique identifier for this component, if one was not passed in.
235-
/// </summary>
236-
/// <returns></returns>
237-
private static string GenerateId()
238-
{
239-
return "react_" + Guid.NewGuid().ToShortGuid();
240-
}
241233
}
242234
}

src/React.Core/ReactEnvironment.cs

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* Copyright (c) 2014-Present, Facebook, Inc.
33
* All rights reserved.
44
*
@@ -55,6 +55,10 @@ public class ReactEnvironment : IReactEnvironment, IDisposable
5555
/// Hash algorithm for file-based cache
5656
/// </summary>
5757
protected readonly IFileCacheHash _fileCacheHash;
58+
/// <summary>
59+
/// React Id generator
60+
/// </summary>
61+
private readonly IReactIdGenerator _reactIdGenerator;
5862

5963
/// <summary>
6064
/// JSX Transformer instance for this environment
@@ -120,19 +124,22 @@ public static IReactEnvironment GetCurrentOrThrow
120124
/// <param name="cache">The cache to use for JSX compilation</param>
121125
/// <param name="fileSystem">File system wrapper</param>
122126
/// <param name="fileCacheHash">Hash algorithm for file-based cache</param>
127+
/// <param name="reactIdGenerator">React ID generator</param>
123128
public ReactEnvironment(
124129
IJavaScriptEngineFactory engineFactory,
125130
IReactSiteConfiguration config,
126131
ICache cache,
127132
IFileSystem fileSystem,
128-
IFileCacheHash fileCacheHash
133+
IFileCacheHash fileCacheHash,
134+
IReactIdGenerator reactIdGenerator
129135
)
130136
{
131137
_engineFactory = engineFactory;
132138
_config = config;
133139
_cache = cache;
134140
_fileSystem = fileSystem;
135141
_fileCacheHash = fileCacheHash;
142+
_reactIdGenerator = reactIdGenerator;
136143
_babelTransformer = new Lazy<IBabel>(() =>
137144
new Babel(this, _cache, _fileSystem, _fileCacheHash, _config)
138145
);
@@ -294,7 +301,7 @@ public virtual IReactComponent CreateComponent<T>(string componentName, T props,
294301
EnsureUserScriptsLoaded();
295302
}
296303

297-
var component = new ReactComponent(this, _config, componentName, containerId)
304+
var component = new ReactComponent(this, _config, _reactIdGenerator, componentName, containerId)
298305
{
299306
Props = props,
300307
ServerOnly = serverOnly

src/React.Core/ReactIdGenerator.cs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2016-Present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
using System;
11+
using System.Threading;
12+
13+
namespace React
14+
{
15+
/// <summary>
16+
/// React ID generator.
17+
/// </summary>
18+
public class ReactIdGenerator : IReactIdGenerator
19+
{
20+
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
21+
22+
private static long _random = DateTime.UtcNow.Ticks;
23+
24+
private static readonly char[] reactPrefix = "react_".ToCharArray();
25+
26+
/// <summary>
27+
/// "react_".Length = 6 + 13 random symbols
28+
/// </summary>
29+
private const int reactIdLength = 19;
30+
31+
[ThreadStatic]
32+
private static char[] _chars;
33+
34+
/// <summary>
35+
/// Returns a short react identifier starts with "react_".
36+
/// </summary>
37+
/// <returns></returns>
38+
public string Generate()
39+
{
40+
var chars = _chars;
41+
if (chars == null)
42+
{
43+
_chars = chars = new char[reactIdLength];
44+
Array.Copy(reactPrefix, 0, chars, 0, reactPrefix.Length);
45+
}
46+
47+
var id = Interlocked.Increment(ref _random);
48+
49+
// from 6 because "react_".Length == 6, _encode32Chars.Length == 32 (base32),
50+
// base32 characters are 5 bits in length and from long (64 bits) we can get 13 symbols
51+
chars[6] = _encode32Chars[(int)(id >> 60) & 31];
52+
chars[7] = _encode32Chars[(int)(id >> 55) & 31];
53+
chars[8] = _encode32Chars[(int)(id >> 50) & 31];
54+
chars[9] = _encode32Chars[(int)(id >> 45) & 31];
55+
chars[10] = _encode32Chars[(int)(id >> 40) & 31];
56+
chars[11] = _encode32Chars[(int)(id >> 35) & 31];
57+
chars[12] = _encode32Chars[(int)(id >> 30) & 31];
58+
chars[13] = _encode32Chars[(int)(id >> 25) & 31];
59+
chars[14] = _encode32Chars[(int)(id >> 20) & 31];
60+
chars[15] = _encode32Chars[(int)(id >> 15) & 31];
61+
chars[16] = _encode32Chars[(int)(id >> 10) & 31];
62+
chars[17] = _encode32Chars[(int)(id >> 5) & 31];
63+
chars[18] = _encode32Chars[(int)id & 31];
64+
65+
return new string(chars, 0, reactIdLength);
66+
}
67+
}
68+
}

src/React.Router/ReactEnvironmentExtensions.cs

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* Copyright (c) 2014-Present, Facebook, Inc.
33
* All rights reserved.
44
*
@@ -35,11 +35,10 @@ public static ReactRouterComponent CreateRouterComponent<T>(
3535
bool clientOnly = false
3636
)
3737
{
38-
var config = AssemblyRegistration.Container.Resolve<IReactSiteConfiguration>();
39-
4038
var component = new ReactRouterComponent(
41-
env,
42-
config,
39+
env,
40+
AssemblyRegistration.Container.Resolve<IReactSiteConfiguration>(),
41+
AssemblyRegistration.Container.Resolve<IReactIdGenerator>(),
4342
componentName,
4443
containerId,
4544
path

src/React.Router/ReactRouterComponent.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@ public class ReactRouterComponent : ReactComponent
2626
/// </summary>
2727
/// <param name="environment">The environment.</param>
2828
/// <param name="configuration">Site-wide configuration.</param>
29+
/// <param name="reactIdGenerator">React Id generator.</param>
2930
/// <param name="componentName">Name of the component.</param>
3031
/// <param name="containerId">The ID of the container DIV for this component</param>
3132
/// <param name="path">F.x. from Request.Path. Used by React Static Router to determine context and routing.</param>
3233
public ReactRouterComponent(
3334
IReactEnvironment environment,
3435
IReactSiteConfiguration configuration,
36+
IReactIdGenerator reactIdGenerator,
3537
string componentName,
3638
string containerId,
3739
string path
38-
) : base(environment, configuration, componentName, containerId)
40+
) : base(environment, configuration, reactIdGenerator, componentName, containerId)
3941
{
4042
_path = path;
4143
}

0 commit comments

Comments
 (0)