Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.3 338 lines 8.6 kB view raw
1/* mpi-div.c - MPI functions 2 * Copyright (C) 1994, 1996 Free Software Foundation, Inc. 3 * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. 4 * 5 * This file is part of GnuPG. 6 * 7 * GnuPG is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * GnuPG is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 20 * 21 * Note: This code is heavily based on the GNU MP Library. 22 * Actually it's the same code with only minor changes in the 23 * way the data is stored; this is to support the abstraction 24 * of an optional secure memory allocation which may be used 25 * to avoid revealing of sensitive data due to paging etc. 26 * The GNU MP Library itself is published under the LGPL; 27 * however I decided to publish this code under the plain GPL. 28 */ 29 30#include <linux/string.h> 31#include "mpi-internal.h" 32#include "longlong.h" 33 34int mpi_fdiv_r(MPI rem, MPI dividend, MPI divisor) 35{ 36 int rc = -ENOMEM; 37 int divisor_sign = divisor->sign; 38 MPI temp_divisor = NULL; 39 40 /* We need the original value of the divisor after the remainder has been 41 * preliminary calculated. We have to copy it to temporary space if it's 42 * the same variable as REM. */ 43 if (rem == divisor) { 44 if (mpi_copy(&temp_divisor, divisor) < 0) 45 goto nomem; 46 divisor = temp_divisor; 47 } 48 49 if (mpi_tdiv_qr(NULL, rem, dividend, divisor) < 0) 50 goto nomem; 51 if (((divisor_sign ? 1 : 0) ^ (dividend->sign ? 1 : 0)) && rem->nlimbs) 52 if (mpi_add(rem, rem, divisor) < 0) 53 goto nomem; 54 55 rc = 0; 56 57nomem: 58 if (temp_divisor) 59 mpi_free(temp_divisor); 60 return rc; 61} 62 63/**************** 64 * Division rounding the quotient towards -infinity. 65 * The remainder gets the same sign as the denominator. 66 * rem is optional 67 */ 68 69ulong mpi_fdiv_r_ui(MPI rem, MPI dividend, ulong divisor) 70{ 71 mpi_limb_t rlimb; 72 73 rlimb = mpihelp_mod_1(dividend->d, dividend->nlimbs, divisor); 74 if (rlimb && dividend->sign) 75 rlimb = divisor - rlimb; 76 77 if (rem) { 78 rem->d[0] = rlimb; 79 rem->nlimbs = rlimb ? 1 : 0; 80 } 81 return rlimb; 82} 83 84int mpi_fdiv_q(MPI quot, MPI dividend, MPI divisor) 85{ 86 MPI tmp = mpi_alloc(mpi_get_nlimbs(quot)); 87 if (!tmp) 88 return -ENOMEM; 89 mpi_fdiv_qr(quot, tmp, dividend, divisor); 90 mpi_free(tmp); 91 return 0; 92} 93 94int mpi_fdiv_qr(MPI quot, MPI rem, MPI dividend, MPI divisor) 95{ 96 int divisor_sign = divisor->sign; 97 MPI temp_divisor = NULL; 98 99 if (quot == divisor || rem == divisor) { 100 if (mpi_copy(&temp_divisor, divisor) < 0) 101 return -ENOMEM; 102 divisor = temp_divisor; 103 } 104 105 if (mpi_tdiv_qr(quot, rem, dividend, divisor) < 0) 106 goto nomem; 107 108 if ((divisor_sign ^ dividend->sign) && rem->nlimbs) { 109 if (mpi_sub_ui(quot, quot, 1) < 0) 110 goto nomem; 111 if (mpi_add(rem, rem, divisor) < 0) 112 goto nomem; 113 } 114 115 if (temp_divisor) 116 mpi_free(temp_divisor); 117 118 return 0; 119 120nomem: 121 mpi_free(temp_divisor); 122 return -ENOMEM; 123} 124 125/* If den == quot, den needs temporary storage. 126 * If den == rem, den needs temporary storage. 127 * If num == quot, num needs temporary storage. 128 * If den has temporary storage, it can be normalized while being copied, 129 * i.e no extra storage should be allocated. 130 */ 131 132int mpi_tdiv_r(MPI rem, MPI num, MPI den) 133{ 134 return mpi_tdiv_qr(NULL, rem, num, den); 135} 136 137int mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den) 138{ 139 int rc = -ENOMEM; 140 mpi_ptr_t np, dp; 141 mpi_ptr_t qp, rp; 142 mpi_size_t nsize = num->nlimbs; 143 mpi_size_t dsize = den->nlimbs; 144 mpi_size_t qsize, rsize; 145 mpi_size_t sign_remainder = num->sign; 146 mpi_size_t sign_quotient = num->sign ^ den->sign; 147 unsigned normalization_steps; 148 mpi_limb_t q_limb; 149 mpi_ptr_t marker[5]; 150 int markidx = 0; 151 152 if (!dsize) 153 return -EINVAL; 154 155 memset(marker, 0, sizeof(marker)); 156 157 /* Ensure space is enough for quotient and remainder. 158 * We need space for an extra limb in the remainder, because it's 159 * up-shifted (normalized) below. */ 160 rsize = nsize + 1; 161 if (mpi_resize(rem, rsize) < 0) 162 goto nomem; 163 164 qsize = rsize - dsize; /* qsize cannot be bigger than this. */ 165 if (qsize <= 0) { 166 if (num != rem) { 167 rem->nlimbs = num->nlimbs; 168 rem->sign = num->sign; 169 MPN_COPY(rem->d, num->d, nsize); 170 } 171 if (quot) { 172 /* This needs to follow the assignment to rem, in case the 173 * numerator and quotient are the same. */ 174 quot->nlimbs = 0; 175 quot->sign = 0; 176 } 177 return 0; 178 } 179 180 if (quot) 181 if (mpi_resize(quot, qsize) < 0) 182 goto nomem; 183 184 /* Read pointers here, when reallocation is finished. */ 185 np = num->d; 186 dp = den->d; 187 rp = rem->d; 188 189 /* Optimize division by a single-limb divisor. */ 190 if (dsize == 1) { 191 mpi_limb_t rlimb; 192 if (quot) { 193 qp = quot->d; 194 rlimb = mpihelp_divmod_1(qp, np, nsize, dp[0]); 195 qsize -= qp[qsize - 1] == 0; 196 quot->nlimbs = qsize; 197 quot->sign = sign_quotient; 198 } else 199 rlimb = mpihelp_mod_1(np, nsize, dp[0]); 200 rp[0] = rlimb; 201 rsize = rlimb != 0 ? 1 : 0; 202 rem->nlimbs = rsize; 203 rem->sign = sign_remainder; 204 return 0; 205 } 206 207 if (quot) { 208 qp = quot->d; 209 /* Make sure QP and NP point to different objects. Otherwise the 210 * numerator would be gradually overwritten by the quotient limbs. */ 211 if (qp == np) { /* Copy NP object to temporary space. */ 212 np = marker[markidx++] = mpi_alloc_limb_space(nsize); 213 if (!np) 214 goto nomem; 215 MPN_COPY(np, qp, nsize); 216 } 217 } else /* Put quotient at top of remainder. */ 218 qp = rp + dsize; 219 220 count_leading_zeros(normalization_steps, dp[dsize - 1]); 221 222 /* Normalize the denominator, i.e. make its most significant bit set by 223 * shifting it NORMALIZATION_STEPS bits to the left. Also shift the 224 * numerator the same number of steps (to keep the quotient the same!). 225 */ 226 if (normalization_steps) { 227 mpi_ptr_t tp; 228 mpi_limb_t nlimb; 229 230 /* Shift up the denominator setting the most significant bit of 231 * the most significant word. Use temporary storage not to clobber 232 * the original contents of the denominator. */ 233 tp = marker[markidx++] = mpi_alloc_limb_space(dsize); 234 if (!tp) 235 goto nomem; 236 mpihelp_lshift(tp, dp, dsize, normalization_steps); 237 dp = tp; 238 239 /* Shift up the numerator, possibly introducing a new most 240 * significant word. Move the shifted numerator in the remainder 241 * meanwhile. */ 242 nlimb = mpihelp_lshift(rp, np, nsize, normalization_steps); 243 if (nlimb) { 244 rp[nsize] = nlimb; 245 rsize = nsize + 1; 246 } else 247 rsize = nsize; 248 } else { 249 /* The denominator is already normalized, as required. Copy it to 250 * temporary space if it overlaps with the quotient or remainder. */ 251 if (dp == rp || (quot && (dp == qp))) { 252 mpi_ptr_t tp; 253 254 tp = marker[markidx++] = mpi_alloc_limb_space(dsize); 255 if (!tp) 256 goto nomem; 257 MPN_COPY(tp, dp, dsize); 258 dp = tp; 259 } 260 261 /* Move the numerator to the remainder. */ 262 if (rp != np) 263 MPN_COPY(rp, np, nsize); 264 265 rsize = nsize; 266 } 267 268 q_limb = mpihelp_divrem(qp, 0, rp, rsize, dp, dsize); 269 270 if (quot) { 271 qsize = rsize - dsize; 272 if (q_limb) { 273 qp[qsize] = q_limb; 274 qsize += 1; 275 } 276 277 quot->nlimbs = qsize; 278 quot->sign = sign_quotient; 279 } 280 281 rsize = dsize; 282 MPN_NORMALIZE(rp, rsize); 283 284 if (normalization_steps && rsize) { 285 mpihelp_rshift(rp, rp, rsize, normalization_steps); 286 rsize -= rp[rsize - 1] == 0 ? 1 : 0; 287 } 288 289 rem->nlimbs = rsize; 290 rem->sign = sign_remainder; 291 292 rc = 0; 293nomem: 294 while (markidx) 295 mpi_free_limb_space(marker[--markidx]); 296 return rc; 297} 298 299int mpi_tdiv_q_2exp(MPI w, MPI u, unsigned count) 300{ 301 mpi_size_t usize, wsize; 302 mpi_size_t limb_cnt; 303 304 usize = u->nlimbs; 305 limb_cnt = count / BITS_PER_MPI_LIMB; 306 wsize = usize - limb_cnt; 307 if (limb_cnt >= usize) 308 w->nlimbs = 0; 309 else { 310 mpi_ptr_t wp; 311 mpi_ptr_t up; 312 313 if (RESIZE_IF_NEEDED(w, wsize) < 0) 314 return -ENOMEM; 315 wp = w->d; 316 up = u->d; 317 318 count %= BITS_PER_MPI_LIMB; 319 if (count) { 320 mpihelp_rshift(wp, up + limb_cnt, wsize, count); 321 wsize -= !wp[wsize - 1]; 322 } else { 323 MPN_COPY_INCR(wp, up + limb_cnt, wsize); 324 } 325 326 w->nlimbs = wsize; 327 } 328 return 0; 329} 330 331/**************** 332 * Check whether dividend is divisible by divisor 333 * (note: divisor must fit into a limb) 334 */ 335int mpi_divisible_ui(MPI dividend, ulong divisor) 336{ 337 return !mpihelp_mod_1(dividend->d, dividend->nlimbs, divisor); 338}