(二)ghostscript源码分析之interp()函数之IREF_NEXT宏分析

interp()函数用了大量的宏。IREF_NEXT只是其中一个宏,但是出现的频率很高。

但是透彻的理解这个宏将为理解interp()函数提供便利。

它的定义形式如下:

#define IREF_NEXT(ip)/ ((const ref_packed *)((const ref *)(ip) + 1))

这句看似简单的代码实际上不那么容易理解,而后面一些列的宏要么与此类似,要么间接的调用它。

而ref的定义是下面这个样子:

struct ref_s { struct tas_s tas; union v { /* name the union to keep gdb happy */ int intval; ushort boolval; float realval; ulong saveid; byte *bytes; const byte *const_bytes; ref *refs; const ref *const_refs; name *pname; const name *const_pname; dict *pdict; const dict *const_pdict; /* * packed is the normal variant for referring to packed arrays, * but we need a writable variant for memory management and for * storing into packed dictionary key arrays. */ const ref_packed *packed; ref_packed *writable_packed; op_proc_t opproc; struct stream_s *pfile; struct gx_device_s *pdevice; obj_header_t *pstruct; } value; };

要使(const ref*)rp +1 变得有有意义,关键在于解析器是怎么处理的。

我们以"{}"为例,来解释它。

识别过程,是在scan_token()函数中办到的,但是具体是怎样办到的?

看下面截取的部分代码片段:

int scan_token(i_ctx_t *i_ctx_p, ref * pref, scanner_state * pstate) { ..................... case '{': /* 比较 {} 和 [] 的执行方式不同 */ if (pstack == 0) { /* outermost procedure */ if_not_spush1() { scan_putback(); scan_type = scanning_none; goto pause_ret; } /* pstack for very first { encountered, */ /* for error recovery */ pdepth = ref_stack_count_inline(&o_stack); } make_int(osp, pstack); pstack = ref_stack_count_inline(&o_stack);/* stack depth when starting current */ /* 从上面可以看出在这种形式下:pdepth != pstack,于是在下面的处理将发生改变 */ if_debug3('S', "[S{]d=%d, s=%d->%d/n", pdepth, (int)osp->value.intval, pstack); goto snext; ......................... case '}': if (pstack == 0) sreturn(e_syntaxerror); osp--; { /* 这是在操作符栈中,对象元素的个数 */ uint size = ref_stack_count_inline(&o_stack) - pstack; ref arr;/* 这样做的目的实际上就想把操作数栈的所有对象放在arr中,指针指向。*/ if_debug4('S', "[S}]d=%d, s=%d->%d, c=%d/n", pdepth, pstack, (pstack == pdepth ? 0 : ref_stack_index(&o_stack, size)->value.intval), size + pstack); myref = (pstack == pdepth ? pref : &arr);/*上面已经解释了,肯定不等。*/ if (check_only) { make_empty_array(myref, 0); ref_stack_pop(&o_stack, size); } else if (ref_array_packing.value.boolval) { retcode = make_packed_array(myref, &o_stack, size, idmemory, "scanner(packed)"); if (retcode < 0) { /* must be VMerror */ osp++; scan_putback(); scan_type = scanning_none; goto pause_ret; } r_set_attrs(myref, a_executable); } else {/*讨论一种易于理解的方式*/ /* 为myref分配内存空间 */ retcode = ialloc_ref_array(myref, a_executable + a_all, size, "scanner(proc)"); if (retcode < 0) { /* must be VMerror */ osp++; scan_putback(); scan_type = scanning_none; goto pause_ret; }//ref_stack_store 这步非常重要:把o_stack中size个对象拷贝到myref中。 retcode = ref_stack_store(&o_stack, myref, size, 0, 1, false, idmemory, "scanner"); if (retcode < 0) { ifree_ref_array(myref, "scanner(proc)"); sreturn(retcode); } /*因为全部拷贝到myref中了,所以把所有的操作符栈中关于过程的对象全部pop*/ ref_stack_pop(&o_stack, size); } if (pstack == pdepth) { /* This was the top-level procedure. */ spop1(); pstack = 0; } else { if (osp < osbot) ref_stack_pop_block(&o_stack); pstack = osp->value.intval; *osp = arr; goto snext; } } break; ............. }

现在到了一个很关键的函数——ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count,
  uint skip, int age, bool check, gs_dual_memory_t *idmemory,
  client_name_t cname);

这个函数能够说明为什么IREF_NEXT能以那样的形式出现是正确的。

这个函数的源代码如下:

int
ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count,
  uint skip, int age, bool check, gs_dual_memory_t *idmemory,
  client_name_t cname)
{
    uint left, pass;
    ref *to;
    ref_stack_enum_t rsenum;

    if (count > ref_stack_count(pstack) || count > r_size(parray))
 return_error(e_rangecheck);
    if (check) {
 int code = ref_stack_store_check(pstack, parray, count, skip);

 if (code < 0)
     return code;
    }
    to = parray->value.refs + count;
    left = count, pass = skip;
    ref_stack_enum_begin(&rsenum, pstack);
    do {
 ref *from = rsenum.ptr;
 uint size = rsenum.size;

 if (size <= pass)
     pass -= size;
 else {
     if (pass != 0)
  size -= pass, pass = 0;
     from += size;
     if (size > left)
  size = left;
     left -= size;
     switch (age) {
     case -1:  /* not an array */
  while (size--) {
      from--, to--;
      ref_assign(to, from);
  }
  break;
     case 0:  /* old array */
  while (size--) {
      from--, to--;
      ref_assign_old(parray, to, from, cname);
  }
  break;
     case 1:  /* new array */
  while (size--) {
      from--, to--;
      ref_assign_new(to, from);
  }
  break;
     }
     if (left == 0)
  break;
 }
    } while (ref_stack_enum_next(&rsenum));
    r_set_size(parray, count);
    return 0;
}

由于传入的age参数是1,所以进入case 1:这个阶段。

加粗的黑体字代码表明了宏所运用的方式是正确的。

这点可以通过运行调试来斧正。

 

你可能感兴趣的:((二)ghostscript源码分析之interp()函数之IREF_NEXT宏分析)