Skip to content

Commit f2aaa76

Browse files
authored
Add SHA256 hashing (#10)
* Add SHA256 hashing * Update ByteString.swift * Update SHA256.swift
1 parent e8f5463 commit f2aaa76

File tree

7 files changed

+536
-1
lines changed

7 files changed

+536
-1
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ Although not visible when writing templates, each underlying value that is passe
493493
* `dropFirst() -> String`: Equivalent to Swift's `.dropFirst()`.
494494
* `dropLast() -> String`: Equivalent to Swift's `.dropLast()`.
495495
* `hash() -> String`: The hash of the string using the `CRC32` algorithm.
496+
* `sha() -> String`: The SHA256 hash of the string using the `SHA256` algorithm.
497+
* DO NOT rely on this to be cryptographically secure.
496498
* `snakeCased() -> String`: Converts the string from camelCase to snake_case.
497499
* `camelCased() -> String`: Converts the string from snake_case to camelCase.
498500
* `withParens() -> String`: If the string is not empty, surrounds it in parenthesis.
@@ -503,6 +505,8 @@ Although not visible when writing templates, each underlying value that is passe
503505
* `isOdd() -> Bool`: Returns whether the integer is odd or not.
504506
* `isEven() -> Bool`: Returns whether the integer is even or not.
505507
* `hash() -> String`: The hash of the string representation of the integer using the `CRC32` algorithm.
508+
* `sha() -> String`: The hash of the string using the `SHA256` algorithm.
509+
* DO NOT rely on this to be cryptographically secure.
506510
* `Array`:
507511
* `first() -> Element?`: Returns the first element of the array.
508512
* `last() -> Element?`: Returns the last element of the array.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
/// Borrowed from https://github.com/swiftlang/swift-tools-support-core/Sources/TSCBasic/ByteString.swift
12+
13+
import Foundation
14+
15+
/// A `ByteString` represents a sequence of bytes.
16+
///
17+
/// This struct provides useful operations for working with buffers of
18+
/// bytes. Conceptually it is just a contiguous array of bytes (UInt8), but it
19+
/// contains methods and default behavior suitable for common operations done
20+
/// using bytes strings.
21+
///
22+
/// This struct *is not* intended to be used for significant mutation of byte
23+
/// strings, we wish to retain the flexibility to micro-optimize the memory
24+
/// allocation of the storage (for example, by inlining the storage for small
25+
/// strings or and by eliminating wasted space in growable arrays). For
26+
/// construction of byte arrays, clients should use the `WritableByteStream` class
27+
/// and then convert to a `ByteString` when complete.
28+
struct ByteString: ExpressibleByArrayLiteral, Hashable, Sendable {
29+
/// The buffer contents.
30+
@usableFromInline
31+
internal var _bytes: [UInt8]
32+
33+
/// Create an empty byte string.
34+
@inlinable
35+
init() {
36+
_bytes = []
37+
}
38+
39+
/// Create a byte string from a byte array literal.
40+
@inlinable
41+
init(arrayLiteral contents: UInt8...) {
42+
_bytes = contents
43+
}
44+
45+
/// Create a byte string from an array of bytes.
46+
@inlinable
47+
init(_ contents: [UInt8]) {
48+
_bytes = contents
49+
}
50+
51+
/// Create a byte string from an array slice.
52+
@inlinable
53+
init(_ contents: ArraySlice<UInt8>) {
54+
_bytes = Array(contents)
55+
}
56+
57+
/// Create a byte string from an byte buffer.
58+
@inlinable
59+
init<S: Sequence> (_ contents: S) where S.Iterator.Element == UInt8 {
60+
_bytes = [UInt8](contents)
61+
}
62+
63+
/// Create a byte string from the UTF8 encoding of a string.
64+
@inlinable
65+
init(encodingAsUTF8 string: String) {
66+
_bytes = [UInt8](string.utf8)
67+
}
68+
69+
/// Access the byte string contents as an array.
70+
@inlinable
71+
var contents: [UInt8] {
72+
return _bytes
73+
}
74+
75+
/// Return the byte string size.
76+
@inlinable
77+
var count: Int {
78+
return _bytes.count
79+
}
80+
81+
/// Gives a non-escaping closure temporary access to an immutable `Data` instance wrapping the `ByteString` without
82+
/// copying any memory around.
83+
///
84+
/// - Parameters:
85+
/// - closure: The closure that will have access to a `Data` instance for the duration of its lifetime.
86+
@inlinable
87+
func withData<T>(_ closure: (Data) throws -> T) rethrows -> T {
88+
return try _bytes.withUnsafeBytes { pointer -> T in
89+
let mutatingPointer = UnsafeMutableRawPointer(mutating: pointer.baseAddress!)
90+
let data = Data(bytesNoCopy: mutatingPointer, count: pointer.count, deallocator: .none)
91+
return try closure(data)
92+
}
93+
}
94+
95+
/// Returns a `String` lowercase hexadecimal representation of the contents of the `ByteString`.
96+
@inlinable
97+
var hexadecimalRepresentation: String {
98+
_bytes.reduce("") {
99+
var str = String($1, radix: 16)
100+
// The above method does not do zero padding.
101+
if str.count == 1 {
102+
str = "0" + str
103+
}
104+
return $0 + str
105+
}
106+
}
107+
108+
/// Returns a `String` lowercase hexadecimal representation of the contents of the `ByteString`.
109+
@inlinable
110+
var decimalRepresentation: String {
111+
_bytes.reduce("") {
112+
var str = String($1, radix: 10)
113+
// The above method does not do zero padding.
114+
if str.count == 1 {
115+
str = "0" + str
116+
}
117+
return $0 + str
118+
}
119+
}
120+
}
121+
122+
/// Conform to CustomDebugStringConvertible.
123+
extension ByteString: CustomStringConvertible {
124+
/// Return the string decoded as a UTF8 sequence, or traps if not possible.
125+
var description: String {
126+
return cString
127+
}
128+
129+
/// Return the string decoded as a UTF8 sequence, if possible.
130+
@available(*, deprecated, message: "Mahdi: Just so it doesn't emit a warning")
131+
@inlinable
132+
var validDescription: String? {
133+
// FIXME: This is very inefficient, we need a way to pass a buffer. It
134+
// is also wrong if the string contains embedded '\0' characters.
135+
let tmp = _bytes + [UInt8(0)]
136+
return tmp.withUnsafeBufferPointer { ptr in
137+
return String(validatingUTF8: unsafeBitCast(ptr.baseAddress, to: UnsafePointer<CChar>.self))
138+
}
139+
}
140+
141+
/// Return the string decoded as a UTF8 sequence, substituting replacement
142+
/// characters for ill-formed UTF8 sequences.
143+
@inlinable
144+
var cString: String {
145+
return String(decoding: _bytes, as: Unicode.UTF8.self)
146+
}
147+
148+
@available(*, deprecated, message: "use description or validDescription instead")
149+
var asString: String? {
150+
return validDescription
151+
}
152+
}
153+
154+
/// StringLiteralConvertable conformance for a ByteString.
155+
extension ByteString: ExpressibleByStringLiteral {
156+
typealias UnicodeScalarLiteralType = StringLiteralType
157+
typealias ExtendedGraphemeClusterLiteralType = StringLiteralType
158+
159+
init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
160+
_bytes = [UInt8](value.utf8)
161+
}
162+
init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
163+
_bytes = [UInt8](value.utf8)
164+
}
165+
init(stringLiteral value: StringLiteralType) {
166+
_bytes = [UInt8](value.utf8)
167+
}
168+
}

Sources/EnumeratorMacroImpl/Types/EInt.swift

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ extension EInt: EMustacheTransformable {
4545
return (self.underlying & 1) == 1
4646
case "hash":
4747
return EString(crc32(self.description.utf8).description)
48+
case "sha":
49+
return EString(SHA256().hash(self.description).decimalRepresentation.prefix(10))
4850
default:
4951
RenderingContext.current.addOrReplaceFunctionDiagnostic(
5052
.invalidTransform(

Sources/EnumeratorMacroImpl/Types/EString.swift

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ extension EString: EMustacheTransformable {
5353
return Bool(self)
5454
case "hash":
5555
return EString(crc32(self.utf8).description)
56+
case "sha":
57+
return EString(SHA256().hash(self.underlying).decimalRepresentation.prefix(10))
5658
case "dropFirst":
5759
return EString(String(self.dropFirst()))
5860
case "dropLast":

0 commit comments

Comments
 (0)