~ubuntu-branches/ubuntu/karmic/gnupg2/karmic-updates

« back to all changes in this revision

Viewing changes to mpi/mpih-mul.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Urlichs
  • Date: 2006-01-24 04:31:42 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060124043142-pbg192or6qxv3yk2
Tags: 1.9.20-1
* New Upstream version. Closes:#306890,#344530
  * Closes:#320490: gpg-protect-tool fails to decrypt PKCS-12 files 
* Depend on libopensc2-dev, not -1-. Closes:#348106

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* mpihelp-mul.c  -  MPI helper functions
 
2
 * Copyright (C) 1994, 1996, 1998, 1999,
 
3
 *               2000 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 <config.h>
 
31
#include <stdio.h>
 
32
#include <stdlib.h>
 
33
#include <string.h>
 
34
#include "mpi-internal.h"
 
35
#include "longlong.h"
 
36
 
 
37
 
 
38
 
 
39
#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
 
40
    do {                                                \
 
41
        if( (size) < KARATSUBA_THRESHOLD )              \
 
42
            mul_n_basecase (prodp, up, vp, size);       \
 
43
        else                                            \
 
44
            mul_n (prodp, up, vp, size, tspace);        \
 
45
    } while (0);
 
46
 
 
47
#define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
 
48
    do {                                            \
 
49
        if ((size) < KARATSUBA_THRESHOLD)           \
 
50
            mpih_sqr_n_basecase (prodp, up, size);       \
 
51
        else                                        \
 
52
            mpih_sqr_n (prodp, up, size, tspace);        \
 
53
    } while (0);
 
54
 
 
55
 
 
56
 
 
57
 
 
58
/* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
 
59
 * both with SIZE limbs, and store the result at PRODP.  2 * SIZE limbs are
 
60
 * always stored.  Return the most significant limb.
 
61
 *
 
62
 * Argument constraints:
 
63
 * 1. PRODP != UP and PRODP != VP, i.e. the destination
 
64
 *    must be distinct from the multiplier and the multiplicand.
 
65
 *
 
66
 *
 
67
 * Handle simple cases with traditional multiplication.
 
68
 *
 
69
 * This is the most critical code of multiplication.  All multiplies rely
 
70
 * on this, both small and huge.  Small ones arrive here immediately.  Huge
 
71
 * ones arrive here as this is the base case for Karatsuba's recursive
 
72
 * algorithm below.
 
73
 */
 
74
 
 
75
static mpi_limb_t
 
76
mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
 
77
                                 mpi_ptr_t vp, mpi_size_t size)
 
78
{
 
79
    mpi_size_t i;
 
80
    mpi_limb_t cy;
 
81
    mpi_limb_t v_limb;
 
82
 
 
83
    /* Multiply by the first limb in V separately, as the result can be
 
84
     * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
 
85
    v_limb = vp[0];
 
86
    if( v_limb <= 1 ) {
 
87
        if( v_limb == 1 )
 
88
            MPN_COPY( prodp, up, size );
 
89
        else
 
90
            MPN_ZERO( prodp, size );
 
91
        cy = 0;
 
92
    }
 
93
    else
 
94
        cy = mpihelp_mul_1( prodp, up, size, v_limb );
 
95
 
 
96
    prodp[size] = cy;
 
97
    prodp++;
 
98
 
 
99
    /* For each iteration in the outer loop, multiply one limb from
 
100
     * U with one limb from V, and add it to PROD.  */
 
101
    for( i = 1; i < size; i++ ) {
 
102
        v_limb = vp[i];
 
103
        if( v_limb <= 1 ) {
 
104
            cy = 0;
 
105
            if( v_limb == 1 )
 
106
               cy = mpihelp_add_n(prodp, prodp, up, size);
 
107
        }
 
108
        else
 
109
            cy = mpihelp_addmul_1(prodp, up, size, v_limb);
 
110
 
 
111
        prodp[size] = cy;
 
112
        prodp++;
 
113
    }
 
114
 
 
115
    return cy;
 
116
}
 
117
 
 
118
 
 
119
static void
 
120
mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
 
121
                        mpi_size_t size, mpi_ptr_t tspace )
 
122
{
 
123
    if( size & 1 ) {
 
124
      /* The size is odd, and the code below doesn't handle that.
 
125
       * Multiply the least significant (size - 1) limbs with a recursive
 
126
       * call, and handle the most significant limb of S1 and S2
 
127
       * separately.
 
128
       * A slightly faster way to do this would be to make the Karatsuba
 
129
       * code below behave as if the size were even, and let it check for
 
130
       * odd size in the end.  I.e., in essence move this code to the end.
 
131
       * Doing so would save us a recursive call, and potentially make the
 
132
       * stack grow a lot less.
 
133
       */
 
134
      mpi_size_t esize = size - 1;       /* even size */
 
135
      mpi_limb_t cy_limb;
 
136
 
 
137
      MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
 
138
      cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, vp[esize] );
 
139
      prodp[esize + esize] = cy_limb;
 
140
      cy_limb = mpihelp_addmul_1( prodp + esize, vp, size, up[esize] );
 
141
      prodp[esize + size] = cy_limb;
 
142
    }
 
143
    else {
 
144
        /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
 
145
         *
 
146
         * Split U in two pieces, U1 and U0, such that
 
147
         * U = U0 + U1*(B**n),
 
148
         * and V in V1 and V0, such that
 
149
         * V = V0 + V1*(B**n).
 
150
         *
 
151
         * UV is then computed recursively using the identity
 
152
         *
 
153
         *        2n   n          n                     n
 
154
         * UV = (B  + B )U V  +  B (U -U )(V -V )  +  (B + 1)U V
 
155
         *                1 1        1  0   0  1              0 0
 
156
         *
 
157
         * Where B = 2**BITS_PER_MP_LIMB.
 
158
         */
 
159
        mpi_size_t hsize = size >> 1;
 
160
        mpi_limb_t cy;
 
161
        int negflg;
 
162
 
 
163
        /* Product H.      ________________  ________________
 
164
         *                |_____U1 x V1____||____U0 x V0_____|
 
165
         * Put result in upper part of PROD and pass low part of TSPACE
 
166
         * as new TSPACE.
 
167
         */
 
168
        MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
 
169
 
 
170
        /* Product M.      ________________
 
171
         *                |_(U1-U0)(V0-V1)_|
 
172
         */
 
173
        if( mpihelp_cmp(up + hsize, up, hsize) >= 0 ) {
 
174
            mpihelp_sub_n(prodp, up + hsize, up, hsize);
 
175
            negflg = 0;
 
176
        }
 
177
        else {
 
178
            mpihelp_sub_n(prodp, up, up + hsize, hsize);
 
179
            negflg = 1;
 
180
        }
 
181
        if( mpihelp_cmp(vp + hsize, vp, hsize) >= 0 ) {
 
182
            mpihelp_sub_n(prodp + hsize, vp + hsize, vp, hsize);
 
183
            negflg ^= 1;
 
184
        }
 
185
        else {
 
186
            mpihelp_sub_n(prodp + hsize, vp, vp + hsize, hsize);
 
187
            /* No change of NEGFLG.  */
 
188
        }
 
189
        /* Read temporary operands from low part of PROD.
 
190
         * Put result in low part of TSPACE using upper part of TSPACE
 
191
         * as new TSPACE.
 
192
         */
 
193
        MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
 
194
 
 
195
        /* Add/copy product H. */
 
196
        MPN_COPY (prodp + hsize, prodp + size, hsize);
 
197
        cy = mpihelp_add_n( prodp + size, prodp + size,
 
198
                            prodp + size + hsize, hsize);
 
199
 
 
200
        /* Add product M (if NEGFLG M is a negative number) */
 
201
        if(negflg)
 
202
            cy -= mpihelp_sub_n(prodp + hsize, prodp + hsize, tspace, size);
 
203
        else
 
204
            cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
 
205
 
 
206
        /* Product L.      ________________  ________________
 
207
         *                |________________||____U0 x V0_____|
 
208
         * Read temporary operands from low part of PROD.
 
209
         * Put result in low part of TSPACE using upper part of TSPACE
 
210
         * as new TSPACE.
 
211
         */
 
212
        MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
 
213
 
 
214
        /* Add/copy Product L (twice) */
 
215
 
 
216
        cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
 
217
        if( cy )
 
218
          mpihelp_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
 
219
 
 
220
        MPN_COPY(prodp, tspace, hsize);
 
221
        cy = mpihelp_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
 
222
        if( cy )
 
223
            mpihelp_add_1(prodp + size, prodp + size, size, 1);
 
224
    }
 
225
}
 
226
 
 
227
 
 
228
void
 
229
mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
 
230
{
 
231
    mpi_size_t i;
 
232
    mpi_limb_t cy_limb;
 
233
    mpi_limb_t v_limb;
 
234
 
 
235
    /* Multiply by the first limb in V separately, as the result can be
 
236
     * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
 
237
    v_limb = up[0];
 
238
    if( v_limb <= 1 ) {
 
239
        if( v_limb == 1 )
 
240
            MPN_COPY( prodp, up, size );
 
241
        else
 
242
            MPN_ZERO(prodp, size);
 
243
        cy_limb = 0;
 
244
    }
 
245
    else
 
246
        cy_limb = mpihelp_mul_1( prodp, up, size, v_limb );
 
247
 
 
248
    prodp[size] = cy_limb;
 
249
    prodp++;
 
250
 
 
251
    /* For each iteration in the outer loop, multiply one limb from
 
252
     * U with one limb from V, and add it to PROD.  */
 
253
    for( i=1; i < size; i++) {
 
254
        v_limb = up[i];
 
255
        if( v_limb <= 1 ) {
 
256
            cy_limb = 0;
 
257
            if( v_limb == 1 )
 
258
                cy_limb = mpihelp_add_n(prodp, prodp, up, size);
 
259
        }
 
260
        else
 
261
            cy_limb = mpihelp_addmul_1(prodp, up, size, v_limb);
 
262
 
 
263
        prodp[size] = cy_limb;
 
264
        prodp++;
 
265
    }
 
266
}
 
267
 
 
268
 
 
269
void
 
270
mpih_sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
 
271
{
 
272
    if( size & 1 ) {
 
273
        /* The size is odd, and the code below doesn't handle that.
 
274
         * Multiply the least significant (size - 1) limbs with a recursive
 
275
         * call, and handle the most significant limb of S1 and S2
 
276
         * separately.
 
277
         * A slightly faster way to do this would be to make the Karatsuba
 
278
         * code below behave as if the size were even, and let it check for
 
279
         * odd size in the end.  I.e., in essence move this code to the end.
 
280
         * Doing so would save us a recursive call, and potentially make the
 
281
         * stack grow a lot less.
 
282
         */
 
283
        mpi_size_t esize = size - 1;       /* even size */
 
284
        mpi_limb_t cy_limb;
 
285
 
 
286
        MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
 
287
        cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, up[esize] );
 
288
        prodp[esize + esize] = cy_limb;
 
289
        cy_limb = mpihelp_addmul_1( prodp + esize, up, size, up[esize] );
 
290
 
 
291
        prodp[esize + size] = cy_limb;
 
292
    }
 
293
    else {
 
294
        mpi_size_t hsize = size >> 1;
 
295
        mpi_limb_t cy;
 
296
 
 
297
        /* Product H.      ________________  ________________
 
298
         *                |_____U1 x U1____||____U0 x U0_____|
 
299
         * Put result in upper part of PROD and pass low part of TSPACE
 
300
         * as new TSPACE.
 
301
         */
 
302
        MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
 
303
 
 
304
        /* Product M.      ________________
 
305
         *                |_(U1-U0)(U0-U1)_|
 
306
         */
 
307
        if( mpihelp_cmp( up + hsize, up, hsize) >= 0 )
 
308
            mpihelp_sub_n( prodp, up + hsize, up, hsize);
 
309
        else
 
310
            mpihelp_sub_n (prodp, up, up + hsize, hsize);
 
311
 
 
312
        /* Read temporary operands from low part of PROD.
 
313
         * Put result in low part of TSPACE using upper part of TSPACE
 
314
         * as new TSPACE.  */
 
315
        MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
 
316
 
 
317
        /* Add/copy product H  */
 
318
        MPN_COPY(prodp + hsize, prodp + size, hsize);
 
319
        cy = mpihelp_add_n(prodp + size, prodp + size,
 
320
                           prodp + size + hsize, hsize);
 
321
 
 
322
        /* Add product M (if NEGFLG M is a negative number).  */
 
323
        cy -= mpihelp_sub_n (prodp + hsize, prodp + hsize, tspace, size);
 
324
 
 
325
        /* Product L.      ________________  ________________
 
326
         *                |________________||____U0 x U0_____|
 
327
         * Read temporary operands from low part of PROD.
 
328
         * Put result in low part of TSPACE using upper part of TSPACE
 
329
         * as new TSPACE.  */
 
330
        MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
 
331
 
 
332
        /* Add/copy Product L (twice).  */
 
333
        cy += mpihelp_add_n (prodp + hsize, prodp + hsize, tspace, size);
 
334
        if( cy )
 
335
            mpihelp_add_1(prodp + hsize + size, prodp + hsize + size,
 
336
                                                            hsize, cy);
 
337
 
 
338
        MPN_COPY(prodp, tspace, hsize);
 
339
        cy = mpihelp_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
 
340
        if( cy )
 
341
            mpihelp_add_1 (prodp + size, prodp + size, size, 1);
 
342
    }
 
343
}
 
344
 
 
345
 
 
346
/* This should be made into an inline function in gmp.h.  */
 
347
void
 
348
mpihelp_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size)
 
349
{
 
350
    int secure;
 
351
 
 
352
    if( up == vp ) {
 
353
        if( size < KARATSUBA_THRESHOLD )
 
354
            mpih_sqr_n_basecase( prodp, up, size );
 
355
        else {
 
356
            mpi_ptr_t tspace;
 
357
            secure = m_is_secure( up );
 
358
            tspace = mpi_alloc_limb_space( 2 * size, secure );
 
359
            mpih_sqr_n( prodp, up, size, tspace );
 
360
            mpi_free_limb_space( tspace );
 
361
        }
 
362
    }
 
363
    else {
 
364
        if( size < KARATSUBA_THRESHOLD )
 
365
            mul_n_basecase( prodp, up, vp, size );
 
366
        else {
 
367
            mpi_ptr_t tspace;
 
368
            secure = m_is_secure( up ) || m_is_secure( vp );
 
369
            tspace = mpi_alloc_limb_space( 2 * size, secure );
 
370
            mul_n (prodp, up, vp, size, tspace);
 
371
            mpi_free_limb_space( tspace );
 
372
        }
 
373
    }
 
374
}
 
375
 
 
376
 
 
377
 
 
378
void
 
379
mpihelp_mul_karatsuba_case( mpi_ptr_t prodp,
 
380
                            mpi_ptr_t up, mpi_size_t usize,
 
381
                            mpi_ptr_t vp, mpi_size_t vsize,
 
382
                            struct karatsuba_ctx *ctx )
 
383
{
 
384
    mpi_limb_t cy;
 
385
 
 
386
    if( !ctx->tspace || ctx->tspace_size < vsize ) {
 
387
        if( ctx->tspace )
 
388
            mpi_free_limb_space( ctx->tspace );
 
389
        ctx->tspace = mpi_alloc_limb_space( 2 * vsize,
 
390
                                       m_is_secure( up ) || m_is_secure( vp ) );
 
391
        ctx->tspace_size = vsize;
 
392
    }
 
393
 
 
394
    MPN_MUL_N_RECURSE( prodp, up, vp, vsize, ctx->tspace );
 
395
 
 
396
    prodp += vsize;
 
397
    up += vsize;
 
398
    usize -= vsize;
 
399
    if( usize >= vsize ) {
 
400
        if( !ctx->tp || ctx->tp_size < vsize ) {
 
401
            if( ctx->tp )
 
402
                mpi_free_limb_space( ctx->tp );
 
403
            ctx->tp = mpi_alloc_limb_space( 2 * vsize, m_is_secure( up )
 
404
                                                      || m_is_secure( vp ) );
 
405
            ctx->tp_size = vsize;
 
406
        }
 
407
 
 
408
        do {
 
409
            MPN_MUL_N_RECURSE( ctx->tp, up, vp, vsize, ctx->tspace );
 
410
            cy = mpihelp_add_n( prodp, prodp, ctx->tp, vsize );
 
411
            mpihelp_add_1( prodp + vsize, ctx->tp + vsize, vsize, cy );
 
412
            prodp += vsize;
 
413
            up += vsize;
 
414
            usize -= vsize;
 
415
        } while( usize >= vsize );
 
416
    }
 
417
 
 
418
    if( usize ) {
 
419
        if( usize < KARATSUBA_THRESHOLD ) {
 
420
            mpihelp_mul( ctx->tspace, vp, vsize, up, usize );
 
421
        }
 
422
        else {
 
423
            if( !ctx->next ) {
 
424
                ctx->next = m_alloc_clear( sizeof *ctx );
 
425
            }
 
426
            mpihelp_mul_karatsuba_case( ctx->tspace,
 
427
                                        vp, vsize,
 
428
                                        up, usize,
 
429
                                        ctx->next );
 
430
        }
 
431
 
 
432
        cy = mpihelp_add_n( prodp, prodp, ctx->tspace, vsize);
 
433
        mpihelp_add_1( prodp + vsize, ctx->tspace + vsize, usize, cy );
 
434
    }
 
435
}
 
436
 
 
437
 
 
438
void
 
439
mpihelp_release_karatsuba_ctx( struct karatsuba_ctx *ctx )
 
440
{
 
441
    struct karatsuba_ctx *ctx2;
 
442
 
 
443
    if( ctx->tp )
 
444
        mpi_free_limb_space( ctx->tp );
 
445
    if( ctx->tspace )
 
446
        mpi_free_limb_space( ctx->tspace );
 
447
    for( ctx=ctx->next; ctx; ctx = ctx2 ) {
 
448
        ctx2 = ctx->next;
 
449
        if( ctx->tp )
 
450
            mpi_free_limb_space( ctx->tp );
 
451
        if( ctx->tspace )
 
452
            mpi_free_limb_space( ctx->tspace );
 
453
        m_free( ctx );
 
454
    }
 
455
}
 
456
 
 
457
/* Multiply the natural numbers u (pointed to by UP, with USIZE limbs)
 
458
 * and v (pointed to by VP, with VSIZE limbs), and store the result at
 
459
 * PRODP.  USIZE + VSIZE limbs are always stored, but if the input
 
460
 * operands are normalized.  Return the most significant limb of the
 
461
 * result.
 
462
 *
 
463
 * NOTE: The space pointed to by PRODP is overwritten before finished
 
464
 * with U and V, so overlap is an error.
 
465
 *
 
466
 * Argument constraints:
 
467
 * 1. USIZE >= VSIZE.
 
468
 * 2. PRODP != UP and PRODP != VP, i.e. the destination
 
469
 *    must be distinct from the multiplier and the multiplicand.
 
470
 */
 
471
 
 
472
mpi_limb_t
 
473
mpihelp_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
 
474
                              mpi_ptr_t vp, mpi_size_t vsize)
 
475
{
 
476
    mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
 
477
    mpi_limb_t cy;
 
478
    struct karatsuba_ctx ctx;
 
479
 
 
480
    if( vsize < KARATSUBA_THRESHOLD ) {
 
481
        mpi_size_t i;
 
482
        mpi_limb_t v_limb;
 
483
 
 
484
        if( !vsize )
 
485
            return 0;
 
486
 
 
487
        /* Multiply by the first limb in V separately, as the result can be
 
488
         * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
 
489
        v_limb = vp[0];
 
490
        if( v_limb <= 1 ) {
 
491
            if( v_limb == 1 )
 
492
                MPN_COPY( prodp, up, usize );
 
493
            else
 
494
                MPN_ZERO( prodp, usize );
 
495
            cy = 0;
 
496
        }
 
497
        else
 
498
            cy = mpihelp_mul_1( prodp, up, usize, v_limb );
 
499
 
 
500
        prodp[usize] = cy;
 
501
        prodp++;
 
502
 
 
503
        /* For each iteration in the outer loop, multiply one limb from
 
504
         * U with one limb from V, and add it to PROD.  */
 
505
        for( i = 1; i < vsize; i++ ) {
 
506
            v_limb = vp[i];
 
507
            if( v_limb <= 1 ) {
 
508
                cy = 0;
 
509
                if( v_limb == 1 )
 
510
                   cy = mpihelp_add_n(prodp, prodp, up, usize);
 
511
            }
 
512
            else
 
513
                cy = mpihelp_addmul_1(prodp, up, usize, v_limb);
 
514
 
 
515
            prodp[usize] = cy;
 
516
            prodp++;
 
517
        }
 
518
 
 
519
        return cy;
 
520
    }
 
521
 
 
522
    memset( &ctx, 0, sizeof ctx );
 
523
    mpihelp_mul_karatsuba_case( prodp, up, usize, vp, vsize, &ctx );
 
524
    mpihelp_release_karatsuba_ctx( &ctx );
 
525
    return *prod_endp;
 
526
}
 
527
 
 
528