Skip to content

Commit e56ff3a

Browse files
committed
README and playground updates
1 parent 3127583 commit e56ff3a

File tree

11 files changed

+247
-310
lines changed

11 files changed

+247
-310
lines changed
Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
//: [Previous](@previous)
22
//: ## Character Sets
33
//:
4-
//: As we've seen in the previous sections, `EntropyString` provides default characters for each of
5-
//: the supported character sets. Let's see what's under the hood.
6-
import EntropyString
4+
//: As we\'ve seen in the previous sections, `EntropyString` provides default characters for each of the supported character sets. Let\'s see what\'s under the hood. The available `CharSet`s are *.charSet64*, *.charSet32*, *.charSet16*, *.charSet8*, *.charSet4* and *.charSet2*. The default characters for each were chosen as follows:
5+
import EntropyString
76

8-
print("Base 64: \(RandomString.characters(for: .charSet64))\n")
9-
//: The call to `RandomString.characters(for:)` returns the characters used for any of the
10-
//: character sets defined by the `RandomString.CharBase enum`. The following code reveals all the
11-
//: character sets.
12-
print("Base 32: \(RandomString.characters(for: .charSet32))\n")
13-
print("Base 16: \(RandomString.characters(for: .charSet16))\n")
14-
print("Base 8: \(RandomString.characters(for: .charSet8))\n")
15-
print("Base 4: \(RandomString.characters(for: .charSet4))\n")
16-
print("Base 2: \(RandomString.characters(for: .charSet2))\n")
7+
print("Base 64 chars: \(CharSet.charSet64.chars)\n")
8+
print("Base 32 chars: \(CharSet.charSet32.chars)\n")
9+
print("Base 16 chars: \(CharSet.charSet16.chars)\n")
10+
print("Base 8 chars: \(CharSet.charSet8.chars)\n")
11+
print("Base 4 chars: \(CharSet.charSet4.chars)\n")
12+
print("Base 2 chars: \(CharSet.charSet2.chars)\n")
1713
//: The default character sets were chosen as follows:
1814
//: - Base 64: **ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_**
1915
//: - The file system and URL safe char set from
@@ -33,7 +29,6 @@ print("Base 2: \(RandomString.characters(for: .charSet2))\n")
3329
//: - Base 2: **01**
3430
//: - Binary
3531
//:
36-
//: You may, of course, want to choose the characters used, which is covered next in [Custom
37-
//: Characters](Custom%20Characters).
32+
//: You may, of course, want to choose the characters used, which is covered next in [Custom Characters](Custom%20Characters).
3833
//:
3934
//: [TOC](Table%20of%20Contents) | [Next](@next)
Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,27 @@
11
//: [Previous](@previous)
22
//: ## Custom Bytes
33
//:
4-
//: As described in [Secure Bytes](Secure%20Bytes), `EntropyString` automatically generates random
5-
//: bytes using either `SecRandomCopyBuf` or `arc4random_buf`. These functions are fine, but you
6-
//: may have a need to provide your own btyes, say for deterministic testing or to use a
7-
//: specialized byte genterator. The `RandomString.entropy(of:using:bytes)` function allows
8-
//: passing in your own bytes to create a string.
4+
//: As described in [Secure Bytes](Secure%20Bytes), `EntropyString` automatically generates random bytes using either `SecRandomCopyBuf` or `arc4random_buf`. These functions are fine, but you may have a need to provide your own btyes for deterministic testing or to use a specialized byte genterator. The function `random.string(bits:using)` allows specifying your own bytes to create a string.
95
//:
10-
//: Suppose we want a string capable of 30 bits of entropy using 32 characters. We pass in 4 bytes
11-
//: (to cover the 30 bits):
12-
import EntropyString
6+
//: Suppose we want a string capable of 30 bits of entropy using 32 characters. We pass in 4 bytes (to cover the 30 bits):
7+
import EntropyString
138

14-
let bytes: RandomString.Bytes = [250, 200, 150, 100]
15-
let string = try! RandomString.entropy(of: 30, using: .charSet32, bytes: bytes)
16-
print("String: \(string)\n")
9+
let random = Random()
10+
let bytes: [UInt8] = [250, 200, 150, 100]
11+
let string = try! random.string(bits: 30, using: bytes)
12+
13+
print("String: \(string)\n")
1714
//: * callout(string): Th7fjL
1815
//:
19-
//: The __bytes__ provided can come from any source. However, the number of bytes must be
20-
//: sufficient to generate the string as described in the [Efficiency](Efficiency) section.
21-
//: `RandomString.entropy(of:using:bytes)` throws `RandomString.RandomError.tooFewBytes` if
22-
//: the string cannot be formed from the passed bytes.
23-
do {
24-
try RandomString.entropy(of: 32, using: .charSet32, bytes: bytes)
25-
}
26-
catch {
27-
print(error)
28-
}
16+
//: The __bytes__ provided can come from any source. However, if the number of bytes is insufficient to generate the string as described in the [Efficiency](Efficiency) section, an `EntropyStringError.tooFewBytes` is thrown.
17+
do {
18+
try random.string(bits: 32, using: bytes)
19+
}
20+
catch {
21+
print(error)
22+
}
2923
//: * callout(error): tooFewBytes
3024
//:
31-
//: Note how the number of bytes needed is dependent on the number of characters in our set.
32-
//: In using a string to represent entropy, we can only have multiples of the bits of entropy per
33-
//: character used. So in the example above, to get at least 32 bits of entropy using a character
34-
//: set of 32 characters (5 bits per char), we'll need enough bytes to cover 35 bits, not 32, so
35-
//: a `tooFewBytes` error is thrown.
25+
//: Note the number of bytes needed is dependent on the number of characters in our set. In using a string to represent entropy, we can only have multiples of the bits of entropy per character used. So in the example above, to get at least 32 bits of entropy using a character set of 32 characters (5 bits per char), we'll need enough bytes to cover 35 bits, not 32, so a `tooFewBytes` error is thrown.
3626
//:
37-
//: [TOC](Table%20of%20Contents)
27+
//: [TOC](Table%20of%20Contents) | [Next](@next)
Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,49 @@
11
//: [Previous](@previous)
22
//: ## Custom Characters
33
//:
4-
//: Being able to easily generate random strings is great, but what if you want to specify your
5-
//: own characters? For example, suppose you want to visualize flipping a coin to produce 10 bits
6-
//: of entropy.
7-
import EntropyString
4+
//: Being able to easily generate random strings is great, but what if you want to specify your own characters? For example, suppose you want to visualize flipping a coin to produce 10 bits of entropy.
5+
import EntropyString
86

9-
let randomString = RandomString()
10-
var flips = randomString.entropy(of: 10, using: .charSet2)
11-
print("flips: \(flips)\n")
7+
let random = Random(.charSet2)
8+
var flips = random.string(bits: 10)
9+
10+
print("flips: \(flips)\n")
1211
//: * callout(flips): 0101001110
1312
//:
14-
//: The resulting string of __0__'s and __1__'s doesn't look quite right. You want to use the
15-
//: characters __H__ and __T__ instead.
16-
try! randomString.use("HT", for: .charSet2)
17-
flips = randomString.entropy(of: 10, using: .charSet2)
18-
print("flips: \(flips)\n")
13+
//: The resulting string of __0__'s and __1__'s doesn't look quite right. Perhaps you want to use the characters __H__ and __T__ instead.
14+
try! random.use("HT")
15+
flips = random.string(bits: 10)
16+
17+
print("flips: \(flips)\n")
1918
//: * callout(flips): HTTTHHTTHH
2019
//:
21-
//: Note that setting custom characters in the above code requires using an *instance* of
22-
//: `RandomString`, wheras in the previous sections we used *class* functions for all calls. The
23-
//: function signatures are the same in each case, but you can't change the static character sets
24-
//: used in the class `RandomString` (i.e., there is no `RandomString.use(_,for:)` function).
25-
//:
26-
//: As another example, we saw in [Character Sets](Character%20Sets) the default characters for
27-
//: charSet 16 are **0123456789abcdef**. Suppose you like uppercase hexadecimal letters instead.
28-
try! randomString.use("0123456789ABCDEF", for: .charSet16)
29-
let hex = randomString.entropy(of: 48, using: .charSet16)
30-
print("hex: \(hex)\n")
20+
//: As another example, we saw in [Character Sets](Character%20Sets) the default characters for `charSet16` are **0123456789abcdef**. Suppose you like uppercase hexadecimal letters instead.
21+
try! random.use("0123456789ABCDEF")
22+
let hex = random.string(bits: 48)
23+
24+
print("hex: \(hex)\n")
3125
//: * callout(hex): 4D20D9AA862C
3226
//:
33-
//: Or suppose you want a random password with numbers, lowercase letters and special characters.
34-
try! randomString.use("1234567890abcdefghijklmnopqrstuvwxyz-=[];,./~!@#$%^&*()_+{}|:<>?", for: .charSet64)
35-
let password = randomString.entropy(of: 64, using: .charSet64)
36-
print("password: \(password)")
37-
//: * callout(password): }4?0x*$o_=w
38-
//:
39-
//: Note that `randomString.use(_,for:)` throws a `RandomStringError` if the number of characters
40-
//: doesn't match the number required for the character set or if the characters are not unique.
41-
do {
42-
try randomString.use("abcdefg", for: .charSet8)
43-
}
44-
catch {
45-
print(error)
46-
}
27+
//: The `Random` constructor allows for three separate cases:
28+
//: - No argument defauls to the `charSet32` characters.
29+
//: - One of six default `CharSet`s can be specified.
30+
//: - A string representing the characters to use can be specified.
31+
//:
32+
//: The 3rd option above will throw an `EntropyStringError` if the characters string isn't appropriate for creating a `CharSet`.
33+
do {
34+
try random.use("abcdefg")
35+
}
36+
catch {
37+
print(error)
38+
}
4739
//: * callout(error): invalidCharCount
4840
//:
49-
do {
50-
try randomString.use("01233210", for: .charSet8)
51-
}
52-
catch {
53-
print(error)
54-
}
41+
do {
42+
try random.use("01233210")
43+
}
44+
catch {
45+
print(error)
46+
}
5547
//: * callout(error): charsNotUnique
5648
//:
5749
//: [TOC](Table%20of%20Contents) | [Next](@next)
Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
11
//: [Previous](@previous)
22
//: ## Efficiency
33
//:
4-
//: To efficiently create random strings, `EntropyString` generates the necessary number of
5-
//: bytes needed for each the string and uses those bytes in a bit shifting scheme to index into
6-
//: a character set. For example, consider generating strings from the `.charSet32` character
7-
//: set. There are __32__ characters in the set, so an index into an array of those characters
8-
//: would be in the range `[0,31]`. Generating a random string of `.charSet32` characters is thus
9-
//: reduced to generating a random sequence of indices in the range `[0,31]`.
10-
//:
11-
//: To generate the indices, `EntropyString` slices just enough bits from the array of bytes to create
12-
//: each index. In the example at hand, 5 bits are needed to create an index in the range
13-
//: `[0,31]`. `EntropyString` processes the byte array 5 bits at a time to create the indices. The first
14-
//: index comes from the first 5 bits of the first byte, the second index comes from the last 3 bits of
15-
//: the first byte combined with the first 2 bits of the second byte, and so on as the byte array is
16-
//: systematically sliced to form indices into the character set. And since bit shifting and addition
17-
//: of byte values is really efficient, this scheme is quite fast.
18-
//:
19-
//: The `EntropyString` scheme is also efficient with regard to the amount of randomness used. Consider
20-
//: the following common Swift solution to generating random strings. To generated a character, an index
21-
//: into the available characters is create using `arc4random_uniform`. The code looks something like:
4+
//: To efficiently create random strings, `EntropyString` generates the necessary number of bytes needed for each the string and uses those bytes in a bit shifting scheme to index into a character set. For example, consider generating strings from the *charSet32* character set. There are __32__ characters in the set, so an index into an array of those characters would be in the range `[0,31]`. Generating a random string of *charSet32* characters is thus reduced to generating a random sequence of indices in the range `[0,31]`.
5+
//:
6+
//: To generate the indices, `EntropyString` slices just enough bits from the array of bytes to create each index. In the example at hand, 5 bits are needed to create an index in the range `[0,31]`. `EntropyString` processes the byte array 5 bits at a time to create the indices. The first index comes from the first 5 bits of the first byte, the second index comes from the last 3 bits of the first byte combined with the first 2 bits of the second byte, and so on as the byte array is systematically sliced to form indices into the character set. And since bit shifting and addition of byte values is really efficient, this scheme is quite fast.
7+
//:
8+
//: The `EntropyString` scheme is also efficient with regard to the amount of randomness used. Consider the following common Swift solution to generating random strings. To generated a character, an index into the available characters is create using `arc4random_uniform`. The code looks something like:
229
//:
2310
//: for _ in 0 ..< len {
2411
//: let offset = Int(arc4random_uniform(charCount))
@@ -27,22 +14,11 @@
2714
//: string += String(char)
2815
//: }
2916
//:
30-
//: In the code above, `arc4random_uniform` generates 32 bits of randomness, returned as an `UInt32`. The
31-
//: returned value is used to create an **index**. Suppose we're creating strings with **len=16** and
32-
//: **charCount=32**. Each **char** consumes 32 bits of randomness (`UInt32`) while only injecting 5 bits
33-
//: (`log2(32)`) of entropy into **string**. The resulting string has an information carrying capacity of
34-
//: 80 bits. So creating each **string** requires a *total* of 512 bits of randomness while only actually
35-
//: *carrying* 80 bits of that entropy forward in the string itself. That means 432 bits (84% of the total!)
36-
//: of the generated randomness is simply wasted.
37-
//:
38-
//: Compare that to the `EntropyString` scheme. For the example above, slicing off 5 bits at a time
39-
//: requires a total of 80 bits (10 bytes). Creating the same strings as above, `EntropyString` uses 80
40-
//: bits of randomness per string with no wasted bits. In general, the `EntropyString` scheme can waste
41-
//: up to 7 bits per string, but that's the worst case scenario and that's *per string*, not *per
42-
//: character*!
43-
//:
44-
//: Fortunately you don't need to really understand how the bytes are efficiently sliced and diced to get
45-
//: the string. But you may want to know that [Secure Bytes](#SecureBytes) are used, and that's the next
17+
//: In the code above, `arc4random_uniform` generates 32 bits of randomness, returned as an `UInt32`. The returned value is used to create **index**. Suppose we're creating strings with **len=16** and **charCount=32**. Each **char** consumes 32 bits of randomness (`UInt32`) while only injecting 5 bits (`log2(32)`) of entropy into **string**. The resulting string has an information carrying capacity of 80 bits. So creating each string requires a *total* of 512 bits of randomness while only actually *carrying* 80 bits of that entropy forward in the string itself. That means 432 bits (84% of the total!) of the generated randomness is simply wasted.
18+
//:
19+
//: Compare that to the `EntropyString` scheme. For the example above, slicing off 5 bits at a time requires a total of 80 bits (10 bytes). Creating the same strings as above, `EntropyString` uses 80 bits of randomness per string with no wasted bits. In general, the `EntropyString` scheme can waste up to 7 bits per string, but that's the worst case scenario and that's *per string*, not *per character*!
20+
//:
21+
//: Fortunately you don't need to really understand how the bytes are efficiently sliced and diced to get the string. But you may want to know that [Secure Bytes](#SecureBytes) are used, and that's the next
4622
//: topic.
4723
//:
4824
//: [TOC](Table%20of%20Contents) | [Next](@next)

0 commit comments

Comments
 (0)