1// sass.hpp must go before all system headers to get the
2// __EXTENSIONS__ fix on Solaris.
3#include "sass.hpp"
4
5#include <cstdint>
6#include <cstdlib>
7#include <cmath>
8#include <random>
9#include <sstream>
10#include <iomanip>
11#include <algorithm>
12
13#include "ast.hpp"
14#include "units.hpp"
15#include "fn_utils.hpp"
16#include "fn_numbers.hpp"
17
18#ifdef __MINGW32__
19#include "windows.h"
20#include "wincrypt.h"
21#endif
22
23namespace Sass {
24
25 namespace Functions {
26
27 #ifdef __MINGW32__
28 uint64_t GetSeed()
29 {
30 HCRYPTPROV hp = 0;
31 BYTE rb[8];
32 CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
33 CryptGenRandom(hp, sizeof(rb), rb);
34 CryptReleaseContext(hp, 0);
35
36 uint64_t seed;
37 memcpy(&seed, &rb[0], sizeof(seed));
38
39 return seed;
40 }
41 #else
42 uint64_t GetSeed()
43 {
44 std::random_device rd;
45 return rd();
46 }
47 #endif
48
49 // note: the performance of many implementations of
50 // random_device degrades sharply once the entropy pool
51 // is exhausted. For practical use, random_device is
52 // generally only used to seed a PRNG such as mt19937.
53 static std::mt19937 rand(static_cast<unsigned int>(GetSeed()));
54
55 ///////////////////
56 // NUMBER FUNCTIONS
57 ///////////////////
58
59 Signature percentage_sig = "percentage($number)";
60 BUILT_IN(percentage)
61 {
62 Number_Obj n = ARGN("$number");
63 if (!n->is_unitless()) error(msg: "argument $number of `" + sass::string(sig) + "` must be unitless", pstate, traces);
64 return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");
65 }
66
67 Signature round_sig = "round($number)";
68 BUILT_IN(round)
69 {
70 Number_Obj r = ARGN("$number");
71 r->value(value__: Sass::round(val: r->value(), precision: ctx.c_options.precision));
72 r->pstate(pstate__: pstate);
73 return r.detach();
74 }
75
76 Signature ceil_sig = "ceil($number)";
77 BUILT_IN(ceil)
78 {
79 Number_Obj r = ARGN("$number");
80 r->value(value__: std::ceil(x: r->value()));
81 r->pstate(pstate__: pstate);
82 return r.detach();
83 }
84
85 Signature floor_sig = "floor($number)";
86 BUILT_IN(floor)
87 {
88 Number_Obj r = ARGN("$number");
89 r->value(value__: std::floor(x: r->value()));
90 r->pstate(pstate__: pstate);
91 return r.detach();
92 }
93
94 Signature abs_sig = "abs($number)";
95 BUILT_IN(abs)
96 {
97 Number_Obj r = ARGN("$number");
98 r->value(value__: std::abs(x: r->value()));
99 r->pstate(pstate__: pstate);
100 return r.detach();
101 }
102
103 Signature min_sig = "min($numbers...)";
104 BUILT_IN(min)
105 {
106 List* arglist = ARG("$numbers", List);
107 Number_Obj least;
108 size_t L = arglist->length();
109 if (L == 0) {
110 error(msg: "At least one argument must be passed.", pstate, traces);
111 }
112 for (size_t i = 0; i < L; ++i) {
113 ExpressionObj val = arglist->value_at_index(i);
114 Number_Obj xi = Cast<Number>(ptr: val);
115 if (!xi) {
116 error(msg: "\"" + val->to_string(opt: ctx.c_options) + "\" is not a number for `min'", pstate, traces);
117 }
118 if (least) {
119 if (*xi < *least) least = xi;
120 } else least = xi;
121 }
122 return least.detach();
123 }
124
125 Signature max_sig = "max($numbers...)";
126 BUILT_IN(max)
127 {
128 List* arglist = ARG("$numbers", List);
129 Number_Obj greatest;
130 size_t L = arglist->length();
131 if (L == 0) {
132 error(msg: "At least one argument must be passed.", pstate, traces);
133 }
134 for (size_t i = 0; i < L; ++i) {
135 ExpressionObj val = arglist->value_at_index(i);
136 Number_Obj xi = Cast<Number>(ptr: val);
137 if (!xi) {
138 error(msg: "\"" + val->to_string(opt: ctx.c_options) + "\" is not a number for `max'", pstate, traces);
139 }
140 if (greatest) {
141 if (*greatest < *xi) greatest = xi;
142 } else greatest = xi;
143 }
144 return greatest.detach();
145 }
146
147 Signature random_sig = "random($limit:false)";
148 BUILT_IN(random)
149 {
150 AST_Node_Obj arg = env["$limit"];
151 Value* v = Cast<Value>(ptr: arg);
152 Number* l = Cast<Number>(ptr: arg);
153 Boolean* b = Cast<Boolean>(ptr: arg);
154 if (l) {
155 double lv = l->value();
156 if (lv < 1) {
157 sass::ostream err;
158 err << "$limit " << lv << " must be greater than or equal to 1 for `random'";
159 error(msg: err.str(), pstate, traces);
160 }
161 bool eq_int = std::fabs(x: trunc(x: lv) - lv) < NUMBER_EPSILON;
162 if (!eq_int) {
163 sass::ostream err;
164 err << "Expected $limit to be an integer but got " << lv << " for `random'";
165 error(msg: err.str(), pstate, traces);
166 }
167 std::uniform_real_distribution<> distributor(1, lv + 1);
168 uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));
169 return SASS_MEMORY_NEW(Number, pstate, (double)distributed);
170 }
171 else if (b) {
172 std::uniform_real_distribution<> distributor(0, 1);
173 double distributed = static_cast<double>(distributor(rand));
174 return SASS_MEMORY_NEW(Number, pstate, distributed);
175 } else if (v) {
176 traces.push_back(x: Backtrace(pstate));
177 throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v);
178 } else {
179 traces.push_back(x: Backtrace(pstate));
180 throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number");
181 }
182 }
183
184 Signature unique_id_sig = "unique-id()";
185 BUILT_IN(unique_id)
186 {
187 sass::ostream ss;
188 std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8
189 uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));
190 ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed;
191 return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str());
192 }
193
194 Signature unit_sig = "unit($number)";
195 BUILT_IN(unit)
196 {
197 Number_Obj arg = ARGN("$number");
198 sass::string str(quote(arg->unit(), q: '"'));
199 return SASS_MEMORY_NEW(String_Quoted, pstate, str);
200 }
201
202 Signature unitless_sig = "unitless($number)";
203 BUILT_IN(unitless)
204 {
205 Number_Obj arg = ARGN("$number");
206 bool unitless = arg->is_unitless();
207 return SASS_MEMORY_NEW(Boolean, pstate, unitless);
208 }
209
210 Signature comparable_sig = "comparable($number1, $number2)";
211 BUILT_IN(comparable)
212 {
213 Number_Obj n1 = ARGN("$number1");
214 Number_Obj n2 = ARGN("$number2");
215 if (n1->is_unitless() || n2->is_unitless()) {
216 return SASS_MEMORY_NEW(Boolean, pstate, true);
217 }
218 // normalize into main units
219 n1->normalize(); n2->normalize();
220 Units &lhs_unit = *n1, &rhs_unit = *n2;
221 bool is_comparable = (lhs_unit == rhs_unit);
222 return SASS_MEMORY_NEW(Boolean, pstate, is_comparable);
223 }
224
225 }
226
227}
228

source code of gtk/subprojects/libsass/src/fn_numbers.cpp