1
2##  sha1 -- SHA1 hash generation for R
3##
4##  Copyright (C) 2015 - 2019  Thierry Onkelinx and Dirk Eddelbuettel
5##  Copyright (C) 2016 - 2019  Viliam Simko
6##
7##  This file is part of digest.
8##
9##  digest is free software: you can redistribute it and/or modify
10##  it under the terms of the GNU General Public License as published by
11##  the Free Software Foundation, either version 2 of the License, or
12##  (at your option) any later version.
13##
14##  digest is distributed in the hope that it will be useful,
15##  but WITHOUT ANY WARRANTY; without even the implied warranty of
16##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17##  GNU General Public License for more details.
18##
19##  You should have received a copy of the GNU General Public License
20##  along with digest.  If not, see <http://www.gnu.org/licenses/>.
21
22
23# functions written by Thierry Onkelinx
24sha1 <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1"){
25    UseMethod("sha1")
26}
27
28sha1.default <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
29    if (is.list(x)) {
30        return(
31            sha1.list(x, digits = digits, zapsmall = zapsmall, ..., algo = algo)
32        )
33    }
34    stop(  							# #nocov start
35        "sha1() has no method for the '",
36        paste(class(x), collapse = "', '"),
37        "' class",
38        call. = FALSE
39    )								# #nocov end
40}
41
42sha1.numeric <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1"){
43    y <- num2hex(
44        x,
45        digits = digits,
46        zapsmall = zapsmall
47    )
48    y <- add_attributes(x, y)
49    attr(y, "digest::sha1") <- attr_sha1(
50        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
51    )
52    digest(y, algo = algo)
53}
54
55sha1.matrix <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1"){
56    # needed to make results comparable between 32-bit and 64-bit
57    if (storage.mode(x) == "double") {
58        y <- matrix( #return a matrix with the same dimensions as x
59            apply(
60                x,
61                2,
62                num2hex,
63                digits = digits,
64                zapsmall = zapsmall
65            ),
66            ncol = ncol(x)
67        )
68        y <- add_attributes(x, y)
69        attr(y, "digest::sha1") <- attr_sha1(
70            x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
71        )
72        digest(y, algo = algo)
73    } else {
74        attr(x, "digest::sha1") <- attr_sha1(
75            x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
76        )
77        digest(x, algo = algo)
78    }
79}
80
81sha1.complex <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
82    # a vector of complex numbers is converted into 2-column matrix (Re,Im)
83    y <- cbind(Re(x), Im(x))
84    y <- add_attributes(x, y)
85    attr(y, "digest::sha1") <- attr_sha1(
86        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
87    )
88    sha1(y, digits = digits, zapsmall = zapsmall, ..., algo = algo)
89}
90
91sha1.Date <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
92    y <- as.numeric(x)
93    y <- add_attributes(x, y)
94    attr(y, "digest::sha1") <- attr_sha1(
95        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
96    )
97    sha1(y, digits = digits, zapsmall = zapsmall, ..., algo = algo)
98}
99
100sha1.array <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
101    # Array x encoded as list of two elements:
102    # 1. lengths of all dimensions of x
103    # 2. all cells of x as a single vector
104    y <- list(
105        dimension = dim(x),
106        value = as.numeric(x))
107    y <- add_attributes(x, y)
108    attr(y, "digest::sha1") <- attr_sha1(
109        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
110    )
111    sha1(y, digits = digits, zapsmall = zapsmall, ..., algo = algo)
112}
113
114sha1.data.frame <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1"){
115    if (length(x)) {
116        # needed to make results comparable between 32-bit and 64-bit
117        y <- vapply(
118            x,
119            sha1,
120            digits = digits,
121            zapsmall = zapsmall,
122            ...,
123            algo = algo,
124            FUN.VALUE = NA_character_
125        )
126    } else {
127        y <- x
128    }
129    y <- add_attributes(x, y)
130    attr(y, "digest::sha1") <- attr_sha1(
131        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
132    )
133    digest(y, algo = algo)
134}
135
136sha1.list <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1"){
137    if (length(x)) {
138        # needed to make results comparable between 32-bit and 64-bit
139        y <- vapply(
140            x,
141            sha1,
142            digits = digits,
143            zapsmall = zapsmall,
144            ...,
145            algo = algo,
146            FUN.VALUE = NA_character_
147        )
148    } else {
149        y <- x
150    }
151    y <- add_attributes(x, y)
152    attr(y, "digest::sha1") <- list(
153        class = class(x),
154        digits = as.integer(digits),
155        zapsmall = as.integer(zapsmall),
156        ... = ...
157    )
158    digest(y, algo = algo)
159}
160
161sha1.POSIXlt <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
162    y <- do.call(
163        data.frame,
164        lapply(unclass(as.POSIXlt(x)), unlist)
165    )
166    y$sec <- num2hex(y$sec, digits = digits, zapsmall = zapsmall)
167    y <- add_attributes(x, y)
168    attr(y, "digest::sha1") <- attr_sha1(
169        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
170    )
171    digest(y, algo = algo)
172}
173
174sha1.POSIXct <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
175    y <- sha1(
176        as.POSIXlt(x),
177        digits = digits,
178        zapsmall = zapsmall,
179        ...,
180        algo = algo
181    )
182    y <- add_attributes(x, y)
183    attr(y, "digest::sha1") <- attr_sha1(
184        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
185    )
186    digest(y, algo = algo)
187}
188
189sha1.anova <- function(x, digits = 4L, zapsmall = 7L, ..., algo = "sha1"){
190    if (digits > 4) {
191        warning("Hash on 32 bit might be different from hash on 64 bit with digits > 4") # #nocov
192    }
193    y <- apply(
194        x,
195        1,
196        num2hex,
197        digits = digits,
198        zapsmall = zapsmall
199    )
200    y <- add_attributes(x, y)
201    attr(y, "digest::sha1") <- attr_sha1(
202        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
203    )
204    digest(y, algo = algo)
205}
206
207sha1.pairlist <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
208    # needed to make results comparable between 32-bit and 64-bit
209    y <- vapply(
210        x,
211        sha1,
212        digits = digits,
213        zapsmall = zapsmall,
214        ...,
215        algo = algo,
216        FUN.VALUE = NA_character_
217    )
218    y <- add_attributes(x, y)
219    attr(y, "digest::sha1") <- attr_sha1(
220        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
221    )
222    digest(y, algo = algo)
223}
224
225sha1.function <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1"){
226    dots <- list(...)
227    if (is.null(dots$environment)) {
228        dots$environment <- TRUE
229    }
230    if (isTRUE(dots$environment)) {
231        y <- list(
232            formals = formals(x),
233            body = as.character(body(x)),
234            environment = digest(environment(x), algo = algo)
235        )
236    } else {
237        y <- list(
238            formals = formals(x),
239            body = as.character(body(x))
240        )
241    }
242    y <- vapply(
243        y,
244        sha1,
245        digits = digits,
246        zapsmall = zapsmall,
247        environment = dots$environment,
248        ... = dots,
249        algo = algo,
250        FUN.VALUE = NA_character_
251    )
252    y <- add_attributes(x, y)
253    attr(y, "digest::sha1") <- attr_sha1(
254        x = y, digits = digits, zapsmall = zapsmall, algo = algo, dots
255    )
256    digest(y, algo = algo)
257}
258
259sha1.formula <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1"){
260    dots <- list(...)
261    if (is.null(dots$environment)) {
262        dots$environment <- TRUE
263    }
264    y <- vapply(
265        x,
266        sha1,
267        digits = digits,
268        zapsmall = zapsmall,
269        ... = dots,
270        algo = algo,
271        FUN.VALUE = NA_character_
272    )
273    if (isTRUE(dots$environment)) {
274        y <- c(
275            y,
276            digest(environment(x), algo = algo)
277        )
278    }
279    y <- add_attributes(x, y)
280    attr(y, "digest::sha1") <- attr_sha1(
281        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
282    )
283    digest(y, algo = algo)
284}
285"sha1.(" <- function(...) {sha1.formula(...)}
286
287add_attributes <- function(x, y) {
288    if (.getsha1PackageVersion() < package_version("0.6.22.2")) {
289        return(y)
290    }
291    extra <- attributes(x)
292    if (package_version("0.6.23.2") <= .getsha1PackageVersion()) {
293        extra <- extra[names(extra) != "srcref"]
294    }
295    attributes(y) <- c(attributes(y), "digest::attributes" = extra)
296    return(y)
297}
298
299# sha1_attr_digest variants ####
300
301sha1_attr_digest <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
302    # attributes can be set on objects calling sha1_attr_digest()
303    attr(x, "digest::sha1") <- attr_sha1(
304        x = x, digits = digits, zapsmall = zapsmall, algo = algo, ...
305    )
306    digest(x, algo = algo)
307}
308
309sha1.call <- function(...) {sha1_attr_digest(...)}
310sha1.character <- function(...) {sha1_attr_digest(...)}
311sha1.factor <- function(...) {sha1_attr_digest(...)}
312sha1.logical <- function(...) {sha1_attr_digest(...)}
313sha1.integer <- function(...) {sha1_attr_digest(...)}
314sha1.raw <- function(...) {sha1_attr_digest(...)}
315
316# sha1_digest variants ####
317
318sha1_digest <- function(x, digits = 14L, zapsmall = 7L, ..., algo = "sha1") {
319    # attributes cannot be set on objects calling sha1_digest()
320    digest(x, algo = algo)
321}
322
323sha1.name <- function(...) {sha1_digest(...)}
324sha1.NULL <- function(...) {sha1_digest(...)}
325
326# Support Functions ####
327
328attr_sha1 <- function(x, digits, zapsmall, algo, ...) {
329    if (algo == "sha1") {
330        return(
331            list(
332                class = class(x),
333                digits = as.integer(digits),
334                zapsmall = as.integer(zapsmall),
335                ...
336            )
337        )
338    }
339    list(
340        class = class(x),
341        digits = as.integer(digits),
342        zapsmall = as.integer(zapsmall),
343        algo = algo,
344        ...
345    )
346}
347
348num2hex <- function(x, digits = 14L, zapsmall = 7L){
349    if (!is.numeric(x)) {
350        stop("x is not numeric")				# #nocov
351    }
352    if (!is.integer(digits)) {
353        if (!all.equal(as.integer(digits), digits)) {
354            stop("digits is not integer")			# #nocov
355        }
356        digits <- as.integer(digits)
357    }
358    if (length(digits) != 1) {
359        stop("digits must contain exactly one integer")		# #nocov
360    }
361    if (digits < 1) {
362        stop("digits must be positive")				# #nocov
363    }
364    if (!is.integer(zapsmall)) {
365        if (!all.equal(as.integer(zapsmall), zapsmall)) {
366            stop("zapsmall is not integer")			# #nocov
367        }
368        zapsmall <- as.integer(zapsmall)
369    }
370    if (length(zapsmall) != 1) {
371        stop("zapsmall must contain exactly one integer")	# #nocov
372    }
373    if (zapsmall < 1) {
374        stop("zapsmall must be positive")			# #nocov
375    }
376    if (length(x) == 0) {
377        return(character(0))
378    }
379    x.na <- is.na(x)
380    if (all(x.na)) {
381        return(x)
382    }
383    x.inf <- is.infinite(x)
384    output <- rep(NA_character_, length(x))
385    output[x.inf & x > 0] <- "Inf"
386    output[x.inf & x < 0] <- "-Inf"
387    # detect "small" numbers
388    x.zero <- !x.na & !x.inf & abs(x) <= (2^floor(log2(10 ^ -zapsmall)))
389    output[x.zero] <- "0"
390    # The calculations for non-na, non-inf, non-zero values are computationally
391    # more intense.  Don't do them unless necessary.
392    x.finite <- !(x.na | x.inf | x.zero)
393    if (!any(x.finite)) {
394        return(output)
395    }
396    x_abs <- abs(x[x.finite])
397    exponent <- floor(log2(x_abs))
398    negative <- c("", "-")[(x[x.finite] < 0) + 1]
399    x.hex <- sprintf("%a", x_abs*2^-exponent)
400    nc_x <- nchar(x.hex)
401    digits.hex <- ceiling(log(10 ^ digits, base = 16))
402    # select mantissa (starting format is 0x1.[0-9a-f]+p[+-][0-9]+), remove the
403    # beginning through the decimal place, including the fact that exact powers
404    # of two will not have a decimal place.
405    # Remove the beginning through the decimal place (if it exists).
406    mask_decimal <- startsWith(x.hex, "0x1.")
407    start_character <- 4 + mask_decimal
408    # select required precision
409    stop_character <- pmin(nc_x - 3, start_character + digits.hex - 1)
410    mantissa <- substring(x.hex, start_character, stop_character)
411    # Drop trailing zeros
412    mantissa <- gsub(x = mantissa, pattern = "0*$", replacement = "")
413    output[x.finite] <- sprintf("%s%s %d", negative, mantissa, exponent)
414    return(output)
415}
416