// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/bigint/bigint-internal.h" #include "src/bigint/vector-arithmetic.h" namespace v8 { namespace bigint { // The classic algorithm: for every part, multiply the accumulator with // the appropriate multiplier, and add the part. O(n²) overall. void ProcessorImpl::FromStringClassic(RWDigits Z, FromStringAccumulator* accumulator) { … } // The fast algorithm: combine parts in a balanced-binary-tree like order: // Multiply-and-add neighboring pairs of parts, then loop, until only one // part is left. The benefit is that the multiplications will have inputs of // similar sizes, which makes them amenable to fast multiplication algorithms. // We have to do more multiplications than the classic algorithm though, // because we also have to multiply the multipliers. // Optimizations: // - We can skip the multiplier for the first part, because we never need it. // - Most multipliers are the same; we can avoid repeated multiplications and // just copy the previous result. (In theory we could even de-dupe them, but // as the parts/multipliers grow, we'll need most of the memory anyway.) // Copied results are marked with a * below. // - We can re-use memory using a system of three buffers whose usage rotates: // - one is considered empty, and is overwritten with the new parts, // - one holds the multipliers (and will be "empty" in the next round), and // - one initially holds the parts and is overwritten with the new multipliers // Parts and multipliers both grow in each iteration, and get fewer, so we // use the space of two adjacent old chunks for one new chunk. // Since the {heap_parts_} vectors has the right size, and so does the // result {Z}, we can use that memory, and only need to allocate one scratch // vector. If the final result ends up in the wrong bucket, we have to copy it // to the correct one. // - We don't have to keep track of the positions and sizes of the chunks, // because we can deduce their precise placement from the iteration index. // // Example, assuming digit_t is 4 bits, fitting one decimal digit: // Initial state: // parts_: 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 // multipliers_: 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 // After the first iteration of the outer loop: // parts: 12 34 56 78 90 12 34 5 // multipliers: 100 *100 *100 *100 *100 *100 10 // After the second iteration: // parts: 1234 5678 9012 345 // multipliers: 10000 *10000 1000 // After the third iteration: // parts: 12345678 9012345 // multipliers: 10000000 // And then there's an obvious last iteration. void ProcessorImpl::FromStringLarge(RWDigits Z, FromStringAccumulator* accumulator) { … } // Specialized algorithms for power-of-two radixes. Designed to work with // {ParsePowerTwo}: {max_multiplier_} isn't saved, but {radix_} is, and // {last_multiplier_} has special meaning, namely the number of unpopulated bits // in the last part. // For these radixes, {parts} already is a list of correct bit sequences, we // just have to put them together in the right way: // - The parts are currently in reversed order. The highest-index parts[i] // will go into Z[0]. // - All parts, possibly except for the last, are maximally populated. // - A maximally populated part stores a non-fractional number of characters, // i.e. the largest fitting multiple of {char_bits} of it is populated. // - The populated bits in a part are at the low end. // - The number of unused bits in the last part is stored in // {accumulator->last_multiplier_}. // // Example: Given the following parts vector, where letters are used to // label bits, bit order is big endian (i.e. [00000101] encodes "5"), // 'x' means "unpopulated", kDigitBits == 8, radix == 8, and char_bits == 3: // // parts[0] -> [xxABCDEF][xxGHIJKL][xxMNOPQR][xxxxxSTU] <- parts[3] // // We have to assemble the following result: // // Z[0] -> [NOPQRSTU][FGHIJKLM][xxxABCDE] <- Z[2] // void ProcessorImpl::FromStringBasePowerOfTwo( RWDigits Z, FromStringAccumulator* accumulator) { … } void ProcessorImpl::FromString(RWDigits Z, FromStringAccumulator* accumulator) { … } Status Processor::FromString(RWDigits Z, FromStringAccumulator* accumulator) { … } } // namespace bigint } // namespace v8