static int ComputeFragmentSizes(const NaClSrpcMessageHeader* header,
                                enum FragmentPosition fragment_position,
                                LengthHeader* fragment_size) {
  size_t byte_count;
  size_t max_user_bytes;

  if (0 == NaClSrpcMaxImcSendmsgSize) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "ComputeFragmentSizes: NaClSrpcModuleInit not called.\n");
    return 0;
  }
  /* NaClSrpcMaxImcSendmsgSize is guaranteed to to avoid underflow. */
  max_user_bytes =
      NaClSrpcMaxImcSendmsgSize - kFragmentOverhead[fragment_position];
  byte_count = (size_t)
      HeaderTotalBytes(header, kFragmentHeaderCount[fragment_position]);
  if (-1 == (ssize_t) byte_count) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "ComputeFragmentSizes: byte_count was incorrect.\n");
    return 0;
  }
  /* NaClSrpcMaxImcSendmsgSize <= NACL_ABI_SIZE_T_MAX, so cast is safe. */
  fragment_size->byte_count = (nacl_abi_size_t)
      size_min(byte_count, max_user_bytes);
  /* SRPC_DESC_MAX <= NACL_ABI_SIZE_T_MAX, so cast is safe. */
  fragment_size->desc_count = (nacl_abi_size_t)
      size_min(SRPC_DESC_MAX, header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH);
  return 1;
}
Esempio n. 2
0
void test_expression(const E& expr, size_t size, Fn&& fn, const char* expression = nullptr)
{
    using T                  = value_type_of<E>;
    ::testo::test_case* test = ::testo::active_test();
    auto&& c                 = ::testo::make_comparison();
    test->check(c <= expr.size() == size, expression);
    if (expr.size() != size)
        return;
    size                     = size_min(size, 200);
    constexpr size_t maxsize = 2 + ilog2(vector_width<T> * 2);
    for (size_t i = 0; i < size;)
    {
        const size_t next_size =
            std::min(prev_poweroftwo(size - i), static_cast<size_t>(1) << (std::rand() % maxsize));

        cswitch(csize<1> << csizeseq<maxsize>, next_size, [&](auto x) {
            constexpr size_t nsize = val_of(decltype(x)());
            ::testo::scope s(as_string("i = ", i));
            test->check(c <= get_elements(expr, cinput, i, vec_shape<T, nsize>()) ==
                            internal::get_fn_value<T, nsize>(i, fn),
                        expression);
        });
        i += next_size;
    }
}
static void ConsumeFragment(NaClSrpcMessageHeader* header,
                            LengthHeader* fragment_size,
                            size_t guaranteed_entries) {
  size_t descs_read;
  /*
   * The caller has already checked that the number of bytes read is sufficient
   * to ensure that the first "guaranteed_entries" iov entries were satisfied.
   * guaranteed_entries is passed as a constant 1 or 2, so cast is safe.
   */
  header->iov += (nacl_abi_size_t) guaranteed_entries;
  header->iov_length -= (nacl_abi_size_t) guaranteed_entries;
  /* Update to reflect the fragment's descriptors that were consumed. */
  descs_read =
      size_min(fragment_size->desc_count,
               header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH);
  /* Post-condition: descs_read <= NACL_ABI_SIZE_T_MAX. */
  header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH -= (nacl_abi_size_t) descs_read;
  header->NACL_SRPC_MESSAGE_HEADER_DESCV += (nacl_abi_size_t) descs_read;
  fragment_size->desc_count -= (nacl_abi_size_t) descs_read;
  /*
   * Update the header and iov vector to reflect which entries are already
   * satisfied.
   */
  while ((header->iov_length > 0) && (fragment_size->byte_count > 0)) {
    size_t bytes_for_this_entry;
    bytes_for_this_entry =
        size_min(header->iov[0].length, fragment_size->byte_count);
    /* Post-condition: bytes_for_this_entry <= NACL_ABI_SIZE_T_MAX. */
    header->iov[0].length -= (nacl_abi_size_t) bytes_for_this_entry;
    fragment_size->byte_count -= (nacl_abi_size_t) bytes_for_this_entry;
    if (header->iov[0].length > 0) {
      /* The fragment was exhausted, but didn't satisfy this iov entry. */
      header->iov[0].base = (char*) header->iov[0].base + bytes_for_this_entry;
      break;
    }
    /* This iov entry was satisfied. Remove it from the vector. */
    header->iov++;
    header->iov_length--;
  }
  if (fragment_size->byte_count > 0) {
    header->flags |= NACL_ABI_RECVMSG_DATA_TRUNCATED;
  }
  if (fragment_size->desc_count > 0) {
    header->flags |= NACL_ABI_RECVMSG_DESC_TRUNCATED;
  }
}
static int BuildFragmentHeader(NaClSrpcMessageHeader* header,
                               LengthHeader* fragment_size,
                               size_t entries_to_skip,
                               NaClSrpcMessageHeader* frag_hdr) {
  size_t i;
  size_t total_bytes = 0;
  const size_t kMaxIovEntries = SIZE_T_MAX / sizeof *frag_hdr->iov;

  if (NACL_ABI_SIZE_T_MAX < header->iov_length) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "BuildFragmentHeader: iov_length too large.\n");
    return 0;
  }
  /* Copy the entire iovec, even though only part may be used. */
  frag_hdr->iov_length = header->iov_length;
  if (kMaxIovEntries < header->iov_length) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "BuildFragmentHeader: iov_length > kMaxIovEntries.\n");
    return 0;
  }
  frag_hdr->iov = (struct NaClImcMsgIoVec*)
      malloc(header->iov_length * sizeof *frag_hdr->iov);
  if (frag_hdr->iov == NULL) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "BuildFragmentHeader: iov malloc failed.\n");
    return 0;
  }
  memcpy(frag_hdr->iov,
         header->iov,
         frag_hdr->iov_length * sizeof *frag_hdr->iov);
  /* Update the iov[i].length entries. */
  for (i = entries_to_skip; i < header->iov_length; ++i) {
    size_t bytes_this_iov =
        size_min(fragment_size->byte_count - total_bytes,
                 frag_hdr->iov[i].length);
    if (bytes_this_iov == 0) {
      /* header->iov_length was checked at entry to make this safe. */
      frag_hdr->iov_length = (nacl_abi_size_t) i;
    }
    frag_hdr->iov[i].length = bytes_this_iov;
    /* Ensure that total_bytes increment doesn't overflow. */
    if (SIZE_T_MAX - bytes_this_iov < total_bytes) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "BuildFragmentHeader: total bytes overflows.\n");
      return 0;
    }
    total_bytes += bytes_this_iov;
  }
  frag_hdr->NACL_SRPC_MESSAGE_HEADER_DESCV =
      header->NACL_SRPC_MESSAGE_HEADER_DESCV;
  frag_hdr->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = fragment_size->desc_count;
  return 1;
}
Esempio n. 5
0
Window::Window(const String & in) :
    _play(false),
    _frame(0),
    _idle_init(false),
    _idle_frame(0)
{
    DJV_DEBUG(String_Format("Window::Window(%%)").arg(int64_t(this)));

    // Initialize.

    try
    {
        _movie.init(in);
    }
    catch (Error in)
    {
        DJV_AUDIO_APP->error(in);
    }

    _widget = new Image_View;
    _play_widget = new Tool_Button("playback_forward");
    _play_widget->type(Tool_Button::TOGGLE);
    _slider = new Int_Slider;

    Layout_V * layout = new Layout_V(this);
    layout->margin(0);
    layout->spacing(0);
    layout->add(_widget);
    layout->stretch(_widget);
    Layout_H * layout_h = new Layout_H(layout);
    layout_h->margin(0);
    layout_h->add(_play_widget);
    layout_h->add(_slider);
    layout_h->stretch(_slider);

    _slider->range(0, static_cast<int>(_movie.info_time().list.size()) - 1);

    dirty();
    size(_movie.info().size + V2i(0, size_min().y));

    frame_update();
    play_update();

    _play_widget->signal.set(this, play_callback);
    _slider->signal.set(this, frame_callback);
    close_signal.set(this, close_callback);

    show();
}
Esempio n. 6
0
void dstr_ncat_dstr(struct dstr *dst, const struct dstr *str, const size_t len)
{
	size_t new_len, in_len;
	if (!str->array || !*str->array || !len)
		return;

	in_len = size_min(len, str->len);
	new_len = dst->len + in_len;

	dstr_ensure_capacity(dst, new_len + 1);
	memcpy(dst->array+dst->len, str->array, in_len);

	dst->len = new_len;
	dst->array[new_len] = 0;
}
Esempio n. 7
0
void dstr_ncopy_dstr(struct dstr *dst, const struct dstr *str, const size_t len)
{
	size_t newlen;

	if (dst->array)
		dstr_free(dst);

	if (!len)
		return;

	newlen = size_min(len, str->len);
	dst->array = bmemdup(str->array, newlen + 1);
	dst->len   = newlen;

	dst->array[newlen] = 0;
}
Esempio n. 8
0
/* fills "absent" positions in range specification based on response body size 
 * returns true if the range is still valid
 * range is valid if its intersection with [0,length-1] is not empty
 */
static int
httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen)
{
    debug(64, 5) ("httpHdrRangeSpecCanonize: have: [%d, %d) len: %d\n",
	spec->offset, spec->offset + spec->length, spec->length);
    if (!known_spec(spec->offset))	/* suffix */
	spec->offset = size_diff(clen, spec->length);
    else if (!known_spec(spec->length))		/* trailer */
	spec->length = size_diff(clen, spec->offset);
    /* we have a "range" now, adjust length if needed */
    assert(known_spec(spec->length));
    assert(known_spec(spec->offset));
    spec->length = size_min(size_diff(clen, spec->offset), spec->length);
    /* check range validity */
    debug(64, 5) ("httpHdrRangeSpecCanonize: done: [%d, %d) len: %d\n",
	spec->offset, spec->offset + spec->length, spec->length);
    return spec->length > 0;
}
Esempio n. 9
0
int
kk_vector_realloc (kk_vector* vec, size_t cap)
{
  if (vec == NULL)
    {
      errno = EINVAL;
      return -1;
    }
  void *newptr = realloc (vec->mem, cap);
  if (newptr == NULL)
    {
      return -1;
    }
  vec->mem = newptr;
  vec->cap = cap;
  vec->len = size_min(vec->len, cap);
  return 0;
}
Esempio n. 10
0
/* Attempts to read the specified number of bytes from the stream, returning the
 * number of bytes read. */
no_ignore size_t
ubik_stream_read(void *dst, struct ubik_stream *src, size_t len)
{
        size_t n;
        switch (src->stream_type)
        {
        case STREAM_TYPE_FILE_R:
                return fread(dst, 1, len, src->file);
        case STREAM_TYPE_FILE_W:
                return 0;
        case STREAM_TYPE_BUFFER:
                n = size_min(len, src->buffer->end - src->buffer->read);
                memcpy(dst, src->buffer->read, n);
                src->buffer->read += n;
                return n;
        case STREAM_TYPE_GENERATOR:
                if (src->gen->read != NULL)
                        return src->gen->read(dst, src->gen, len);
                return 0;
        }
        return 0;
}
Esempio n. 11
0
no_ignore size_t
ubik_stream_drop(struct ubik_stream *src, size_t len)
{
        size_t n;
        switch (src->stream_type)
        {
        case STREAM_TYPE_FILE_R:
                if (fseek(src->file, len, SEEK_CUR) == 0)
                        return len;
                return 0;
        case STREAM_TYPE_FILE_W:
                return 0;
        case STREAM_TYPE_BUFFER:
                n = size_min(len, src->buffer->write - src->buffer->read);
                src->buffer->read += n;
                return n;
        case STREAM_TYPE_GENERATOR:
                if (src->gen->drop != NULL)
                        return src->gen->drop(src->gen, len);
                return 0;
        }
        return 0;
}
Esempio n. 12
0
/* Warning: sizeof(nacl_abi_off_t)!=sizeof(off_t) on OSX */
int32_t NaClSysMmapIntern(struct NaClApp        *nap,
                          void                  *start,
                          size_t                length,
                          int                   prot,
                          int                   flags,
                          int                   d,
                          nacl_abi_off_t        offset) {
  int                         allowed_flags;
  struct NaClDesc             *ndp;
  uintptr_t                   usraddr;
  uintptr_t                   usrpage;
  uintptr_t                   sysaddr;
  uintptr_t                   endaddr;
  int                         mapping_code;
  uintptr_t                   map_result;
  int                         holding_app_lock;
  struct nacl_abi_stat        stbuf;
  size_t                      alloc_rounded_length;
  nacl_off64_t                file_size;
  nacl_off64_t                file_bytes;
  nacl_off64_t                host_rounded_file_bytes;
  size_t                      alloc_rounded_file_bytes;
  uint32_t                    val_flags;

  holding_app_lock = 0;
  ndp = NULL;

  allowed_flags = (NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED
                   | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS);

  usraddr = (uintptr_t) start;

  if (0 != (flags & ~allowed_flags)) {
    NaClLog(2, "invalid mmap flags 0%o, ignoring extraneous bits\n", flags);
    flags &= allowed_flags;
  }

  if (0 != (flags & NACL_ABI_MAP_ANONYMOUS)) {
    /*
     * anonymous mmap, so backing store is just swap: no descriptor is
     * involved, and no memory object will be created to represent the
     * descriptor.
     */
    ndp = NULL;
  } else {
    ndp = NaClAppGetDesc(nap, d);
    if (NULL == ndp) {
      map_result = -NACL_ABI_EBADF;
      goto cleanup;
    }
  }

  mapping_code = 0;
  /*
   * Check if application is trying to do dynamic code loading by
   * mmaping a file.
   */
  if (0 != (NACL_ABI_PROT_EXEC & prot) &&
      0 != (NACL_ABI_MAP_FIXED & flags) &&
      NULL != ndp &&
      NaClSysCommonAddrRangeInAllowedDynamicCodeSpace(nap, usraddr, length)) {
    if (!nap->enable_dyncode_syscalls) {
      NaClLog(LOG_WARNING,
              "NaClSysMmap: PROT_EXEC when dyncode syscalls are disabled.\n");
      map_result = -NACL_ABI_EINVAL;
      goto cleanup;
    }
    if (0 != (NACL_ABI_PROT_WRITE & prot)) {
      NaClLog(3,
              "NaClSysMmap: asked for writable and executable code pages?!?\n");
      map_result = -NACL_ABI_EINVAL;
      goto cleanup;
    }
    mapping_code = 1;
  } else if (0 != (prot & NACL_ABI_PROT_EXEC)) {
    map_result = -NACL_ABI_EINVAL;
    goto cleanup;
  }

  /*
   * Starting address must be aligned to worst-case allocation
   * granularity.  (Windows.)
   */
  if (!NaClIsAllocPageMultiple(usraddr)) {
    if ((NACL_ABI_MAP_FIXED & flags) != 0) {
      NaClLog(2, "NaClSysMmap: address not allocation granularity aligned\n");
      map_result = -NACL_ABI_EINVAL;
      goto cleanup;
    } else {
      NaClLog(2, "NaClSysMmap: Force alignment of misaligned hint address\n");
      usraddr = NaClTruncAllocPage(usraddr);
    }
  }
  /*
   * Offset should be non-negative (nacl_abi_off_t is signed).  This
   * condition is caught when the file is stat'd and checked, and
   * offset is ignored for anonymous mappings.
   */
  if (offset < 0) {
    NaClLog(1,  /* application bug */
            "NaClSysMmap: negative file offset: %"NACL_PRId64"\n",
            (int64_t) offset);
    map_result = -NACL_ABI_EINVAL;
    goto cleanup;
  }
  /*
   * And offset must be a multiple of the allocation unit.
   */
  if (!NaClIsAllocPageMultiple((uintptr_t) offset)) {
    NaClLog(1,
            ("NaClSysMmap: file offset 0x%08"NACL_PRIxPTR" not multiple"
             " of allocation size\n"),
            (uintptr_t) offset);
    map_result = -NACL_ABI_EINVAL;
    goto cleanup;
  }

  /*
   * Round up to a page size multiple.
   *
   * Note that if length > 0xffff0000 (i.e. -NACL_MAP_PAGESIZE), rounding
   * up the length will wrap around to 0.  We check for length == 0 *after*
   * rounding up the length to simultaneously check for the length
   * originally being 0 and check for the wraparound.
   */
  alloc_rounded_length = NaClRoundAllocPage(length);
  if (alloc_rounded_length != length) {
    if (mapping_code) {
      NaClLog(3, "NaClSysMmap: length not a multiple of allocation size\n");
      map_result = -NACL_ABI_EINVAL;
      goto cleanup;
    }
    NaClLog(1,
            "NaClSysMmap: rounded length to 0x%"NACL_PRIxS"\n",
            alloc_rounded_length);
  }
  if (0 == (uint32_t) alloc_rounded_length) {
    map_result = -NACL_ABI_EINVAL;
    goto cleanup;
  }
  /*
   * Sanity check in case any later code behaves badly if
   * |alloc_rounded_length| is >=4GB.  This check shouldn't fail
   * because |length| was <4GB and we've already checked for overflow
   * when rounding it up.
   * TODO(mseaborn): Remove the need for this by using uint32_t for
   * untrusted sizes more consistently.
   */
  CHECK(alloc_rounded_length == (uint32_t) alloc_rounded_length);

  if (NULL == ndp) {
    /*
     * Note: sentinel values are bigger than the NaCl module addr space.
     */
    file_size                = kMaxUsableFileSize;
    file_bytes               = kMaxUsableFileSize;
    host_rounded_file_bytes  = kMaxUsableFileSize;
    alloc_rounded_file_bytes = kMaxUsableFileSize;
  } else {
    /*
     * We stat the file to figure out its actual size.
     *
     * This is necessary because the POSIXy interface we provide
     * allows mapping beyond the extent of a file but Windows'
     * interface does not.  We simulate the POSIX behaviour on
     * Windows.
     */
    map_result = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
                  Fstat)(ndp, &stbuf);
    if (0 != map_result) {
      goto cleanup;
    }

    /*
     * Preemptively refuse to map anything that's not a regular file or
     * shared memory segment.  Other types usually report st_size of zero,
     * which the code below will handle by just doing a dummy PROT_NONE
     * mapping for the requested size and never attempting the underlying
     * NaClDesc Map operation.  So without this check, the host OS never
     * gets the chance to refuse the mapping operation on an object that
     * can't do it.
     */
    if (!NACL_ABI_S_ISREG(stbuf.nacl_abi_st_mode) &&
        !NACL_ABI_S_ISSHM(stbuf.nacl_abi_st_mode)) {
      map_result = -NACL_ABI_ENODEV;
      goto cleanup;
    }

    /*
     * BUG(bsy): there's a race between this fstat and the actual mmap
     * below.  It's probably insoluble.  Even if we fstat again after
     * mmap and compared, the mmap could have "seen" the file with a
     * different size, after which the racing thread restored back to
     * the same value before the 2nd fstat takes place.
     */
    file_size = stbuf.nacl_abi_st_size;

    if (file_size < offset) {
      map_result = -NACL_ABI_EINVAL;
      goto cleanup;
    }

    file_bytes = file_size - offset;
    if ((nacl_off64_t) kMaxUsableFileSize < file_bytes) {
      host_rounded_file_bytes = kMaxUsableFileSize;
    } else {
      host_rounded_file_bytes = NaClRoundHostAllocPage((size_t) file_bytes);
    }

    ASSERT(host_rounded_file_bytes <= (nacl_off64_t) kMaxUsableFileSize);
    /*
     * We need to deal with NaClRoundHostAllocPage rounding up to zero
     * from ~0u - n, where n < 4096 or 65536 (== 1 alloc page).
     *
     * Luckily, file_bytes is at most kMaxUsableFileSize which is
     * smaller than SIZE_T_MAX, so it should never happen, but we
     * leave the explicit check below as defensive programming.
     */
    alloc_rounded_file_bytes =
      NaClRoundAllocPage((size_t) host_rounded_file_bytes);

    if (0 == alloc_rounded_file_bytes && 0 != host_rounded_file_bytes) {
      map_result = -NACL_ABI_ENOMEM;
      goto cleanup;
    }

    /*
     * NB: host_rounded_file_bytes and alloc_rounded_file_bytes can be
     * zero.  Such an mmap just makes memory (offset relative to
     * usraddr) in the range [0, alloc_rounded_length) inaccessible.
     */
  }

  /*
   * host_rounded_file_bytes is how many bytes we can map from the
   * file, given the user-supplied starting offset.  It is at least
   * one page.  If it came from a real file, it is a multiple of
   * host-OS allocation size.  it cannot be larger than
   * kMaxUsableFileSize.
   */
  if (mapping_code && (size_t) file_bytes < alloc_rounded_length) {
    NaClLog(3,
            "NaClSysMmap: disallowing partial allocation page extension for"
            " short files\n");
    map_result = -NACL_ABI_EINVAL;
    goto cleanup;
  }
  length = size_min(alloc_rounded_length, (size_t) host_rounded_file_bytes);

  /*
   * Lock the addr space.
   */
  NaClXMutexLock(&nap->mu);

  NaClVmHoleOpeningMu(nap);

  holding_app_lock = 1;

  if (0 == (flags & NACL_ABI_MAP_FIXED)) {
    /*
     * The user wants us to pick an address range.
     */
    if (0 == usraddr) {
      /*
       * Pick a hole in addr space of appropriate size, anywhere.
       * We pick one that's best for the system.
       */
      usrpage = NaClVmmapFindMapSpace(&nap->mem_map,
                                      alloc_rounded_length >> NACL_PAGESHIFT);
      NaClLog(4, "NaClSysMmap: FindMapSpace: page 0x%05"NACL_PRIxPTR"\n",
              usrpage);
      if (0 == usrpage) {
        map_result = -NACL_ABI_ENOMEM;
        goto cleanup;
      }
      usraddr = usrpage << NACL_PAGESHIFT;
      NaClLog(4, "NaClSysMmap: new starting addr: 0x%08"NACL_PRIxPTR
              "\n", usraddr);
    } else {
Esempio n. 13
0
/* Use the predecessors in the given map to write the BFS levels to the high 16
 * bits of each element in pred; this also catches some problems in pred
 * itself.  Returns true if the predecessor map is valid. */
static int build_bfs_depth_map(const int64_t nglobalverts, const size_t nlocalverts, const size_t maxlocalverts, const int64_t root, int64_t* const pred) {
  (void)nglobalverts;
  int validation_passed = 1;
  int root_owner;

  size_t root_local;
  get_vertex_distribution_for_pred(1, &root, &root_owner, &root_local);
  int root_is_mine = (root_owner == rank);
  if (root_is_mine) assert (root_local < nlocalverts);

  {
    ptrdiff_t i;
#pragma omp parallel for
    for (i = 0; i < (ptrdiff_t)nlocalverts; ++i) write_pred_entry_depth(&pred[i], UINT16_MAX);
    if (root_is_mine) write_pred_entry_depth(&pred[root_local], 0);
  }
  int64_t* restrict pred_pred = (int64_t*)xMPI_Alloc_mem(size_min(CHUNKSIZE, nlocalverts) * sizeof(int64_t)); /* Predecessor info of predecessor vertex for each local vertex */
  gather* pred_win = init_gather((void*)pred, nlocalverts, sizeof(int64_t), pred_pred, size_min(CHUNKSIZE, nlocalverts), size_min(CHUNKSIZE, nlocalverts), MPI_INT64_T);
  int64_t* restrict pred_vtx = (int64_t*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(int64_t)); /* Vertex (not depth) part of pred map */
  int* restrict pred_owner = (int*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(int));
  size_t* restrict pred_local = (size_t*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(size_t));
  int iter_number = 0;
  {
    /* Iteratively update depth[v] = min(depth[v], depth[pred[v]] + 1) [saturating at UINT16_MAX] until no changes. */
    while (1) {
      ++iter_number;
      int any_changes = 0;
      ptrdiff_t ii;
      for (ii = 0; ii < (ptrdiff_t)maxlocalverts; ii += CHUNKSIZE) {
        ptrdiff_t i_start = ptrdiff_min(ii, nlocalverts);
        ptrdiff_t i_end = ptrdiff_min(ii + CHUNKSIZE, nlocalverts);
        begin_gather(pred_win);
        ptrdiff_t i;
        assert (i_start >= 0 && i_start <= (ptrdiff_t)nlocalverts);
        assert (i_end >= 0 && i_end <= (ptrdiff_t)nlocalverts);
#pragma omp parallel for
        for (i = i_start; i < i_end; ++i) {
          pred_vtx[i - i_start] = get_pred_from_pred_entry(pred[i]);
        }
        get_vertex_distribution_for_pred(i_end - i_start, pred_vtx, pred_owner, pred_local);
#pragma omp parallel for
        for (i = i_start; i < i_end; ++i) {
          if (pred[i] != -1) {
            add_gather_request(pred_win, i - i_start, pred_owner[i - i_start], pred_local[i - i_start], i - i_start); //shit happened here first
          } else {
            pred_pred[i - i_start] = -1;
          }
        }
        end_gather(pred_win);

#pragma omp parallel for reduction(&&:validation_passed) reduction(||:any_changes)
        for (i = i_start; i < i_end; ++i) {
          if (rank == root_owner && (size_t)i == root_local) continue;
          if (get_depth_from_pred_entry(pred_pred[i - i_start]) != UINT16_MAX) {
            if (get_depth_from_pred_entry(pred[i]) != UINT16_MAX && get_depth_from_pred_entry(pred[i]) != get_depth_from_pred_entry(pred_pred[i - i_start]) + 1) {
              fprintf(stderr, "%d: Validation error: BFS predecessors do not form a tree; see vertices %" PRId64 " (depth %" PRIu16 ") and %" PRId64 " (depth %" PRIu16 ").\n", rank, vertex_to_global_for_pred(rank, i), get_depth_from_pred_entry(pred[i]), get_pred_from_pred_entry(pred[i]), get_depth_from_pred_entry(pred_pred[i - i_start]));
              validation_passed = 0;
            } else if (get_depth_from_pred_entry(pred[i]) == get_depth_from_pred_entry(pred_pred[i - i_start]) + 1) {
              /* Nothing to do */
            } else {
              write_pred_entry_depth(&pred[i], get_depth_from_pred_entry(pred_pred[i - i_start]) + 1);
              any_changes = 1;
            }
          }
        }
      }
      MPI_Allreduce(MPI_IN_PLACE, &any_changes, 1, MPI_INT, MPI_LOR, MPI_COMM_WORLD);
      if (!any_changes) break;
    }
  }
  destroy_gather(pred_win);
  MPI_Free_mem(pred_pred);
  free(pred_owner);
  free(pred_local);
  free(pred_vtx);
  return validation_passed;
}
Esempio n. 14
0
/* Returns true if result is valid.  Also, updates high 16 bits of each element
 * of pred to contain the BFS level number (or -1 if not visited) of each
 * vertex; this is based on the predecessor map if the user didn't provide it.
 * */
int validate_bfs_result(const tuple_graph* const tg, const int64_t nglobalverts, const size_t nlocalverts, const int64_t root, int64_t* const pred, int64_t* const edge_visit_count_ptr) {

  assert (tg->edgememory_size >= 0 && tg->max_edgememory_size >= tg->edgememory_size && tg->max_edgememory_size <= tg->nglobaledges);
  assert (pred);
  *edge_visit_count_ptr = 0; /* Ensure it is a valid pointer */
  int ranges_ok = check_value_ranges(nglobalverts, nlocalverts, pred);
  if (root < 0 || root >= nglobalverts) {
    fprintf(stderr, "%d: Validation error: root vertex %" PRId64 " is invalid.\n", rank, root);
    ranges_ok = 0;
  }
  if (!ranges_ok) return 0; /* Fail */

  assert (tg->edgememory_size >= 0 && tg->max_edgememory_size >= tg->edgememory_size && tg->max_edgememory_size <= tg->nglobaledges);
  assert (pred);

  int validation_passed = 1;
  int root_owner;
  size_t root_local;
  get_vertex_distribution_for_pred(1, &root, &root_owner, &root_local);
  int root_is_mine = (root_owner == rank);

  /* Get maximum values so loop counts are consistent across ranks. */
  uint64_t maxlocalverts_ui = nlocalverts;
  MPI_Allreduce(MPI_IN_PLACE, &maxlocalverts_ui, 1, MPI_UINT64_T, MPI_MAX, MPI_COMM_WORLD);
  size_t maxlocalverts = (size_t)maxlocalverts_ui;

  ptrdiff_t max_bufsize = tuple_graph_max_bufsize(tg);
  ptrdiff_t edge_chunk_size = ptrdiff_min(HALF_CHUNKSIZE, max_bufsize);

  assert (tg->edgememory_size >= 0 && tg->max_edgememory_size >= tg->edgememory_size && tg->max_edgememory_size <= tg->nglobaledges);
  assert (pred);

  /* Check that root is its own parent. */
  if (root_is_mine) {
    assert (root_local < nlocalverts);
    if (get_pred_from_pred_entry(pred[root_local]) != root) {
      fprintf(stderr, "%d: Validation error: parent of root vertex %" PRId64 " is %" PRId64 ", not the root itself.\n", rank, root, get_pred_from_pred_entry(pred[root_local]));
      validation_passed = 0;
    }
  }

  assert (tg->edgememory_size >= 0 && tg->max_edgememory_size >= tg->edgememory_size && tg->max_edgememory_size <= tg->nglobaledges);
  assert (pred);
  
  /* Check that nothing else is its own parent. */
  {
    int* restrict pred_owner = (int*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(int));
    size_t* restrict pred_local = (size_t*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(size_t));
    int64_t* restrict pred_vtx = (int64_t*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(int64_t)); /* Vertex (not depth) part of pred map */
    ptrdiff_t ii;
    for (ii = 0; ii < (ptrdiff_t)nlocalverts; ii += CHUNKSIZE) {
      ptrdiff_t i_start = ii;
      ptrdiff_t i_end = ptrdiff_min(ii + CHUNKSIZE, nlocalverts);
      ptrdiff_t i;
      assert (i_start >= 0 && i_start <= (ptrdiff_t)nlocalverts);
      assert (i_end >= 0 && i_end <= (ptrdiff_t)nlocalverts);
#pragma omp parallel for
      for (i = i_start; i < i_end; ++i) {
        pred_vtx[i - i_start] = get_pred_from_pred_entry(pred[i]);
      }
      get_vertex_distribution_for_pred(i_end - i_start, pred_vtx, pred_owner, pred_local);
#pragma omp parallel for reduction(&&:validation_passed)
      for (i = i_start; i < i_end; ++i) {

        if ((!root_is_mine || (size_t)i != root_local) &&
            get_pred_from_pred_entry(pred[i]) != -1 &&
            pred_owner[i - i_start] == rank &&
            pred_local[i - i_start] == (size_t)i) {
          fprintf(stderr, "%d: Validation error: parent of non-root vertex %" PRId64 " is itself.\n", rank, vertex_to_global_for_pred(rank, i));
          validation_passed = 0;
        }
      }
    }
    free(pred_owner);
    free(pred_local);
    free(pred_vtx);
  }

  assert (tg->edgememory_size >= 0 && tg->max_edgememory_size >= tg->edgememory_size && tg->max_edgememory_size <= tg->nglobaledges);
  assert (pred);

  if (bfs_writes_depth_map()) {
    int check_ok = check_bfs_depth_map_using_predecessors(tg, nglobalverts, nlocalverts, maxlocalverts, root, pred);
    if (!check_ok) validation_passed = 0;
  } else {
    
    /* Create a vertex depth map to use for later validation. */
    int pred_ok = build_bfs_depth_map(nglobalverts, nlocalverts, maxlocalverts, root, pred); //shit happened here
    if (!pred_ok) validation_passed = 0;
  }

  {
    /* Check that all edges connect vertices whose depths differ by at most
     * one, and check that there is an edge from each vertex to its claimed
     * predecessor.  Also, count visited edges (including duplicates and
     * self-loops).  */
    unsigned char* restrict pred_valid = (unsigned char*)xMPI_Alloc_mem(nlocalverts * sizeof(unsigned char));
    memset(pred_valid, 0, nlocalverts * sizeof(unsigned char));
    int64_t* restrict edge_endpoint = (int64_t*)xmalloc(2 * edge_chunk_size * sizeof(int64_t));
    int* restrict edge_owner = (int*)xmalloc(2 * edge_chunk_size * sizeof(int));
    size_t* restrict edge_local = (size_t*)xmalloc(2 * edge_chunk_size * sizeof(size_t));
    int64_t* restrict edge_preds = (int64_t*)xMPI_Alloc_mem(2 * edge_chunk_size * sizeof(int64_t));
    gather* pred_win = init_gather((void*)pred, nlocalverts, sizeof(int64_t), edge_preds, 2 * edge_chunk_size, 2 * edge_chunk_size, MPI_INT64_T);
    unsigned char one = 1;
    scatter_constant* pred_valid_win = init_scatter_constant((void*)pred_valid, nlocalverts, sizeof(unsigned char), &one, 2 * edge_chunk_size, MPI_UNSIGNED_CHAR);
    int64_t edge_visit_count = 0;
    ITERATE_TUPLE_GRAPH_BEGIN(tg, buf, bufsize) {
      ptrdiff_t ii;
      for (ii = 0; ii < max_bufsize; ii += HALF_CHUNKSIZE) {
        ptrdiff_t i_start = ptrdiff_min(ii, bufsize);
        ptrdiff_t i_end = ptrdiff_min(ii + HALF_CHUNKSIZE, bufsize);
        assert (i_end - i_start <= edge_chunk_size);
        ptrdiff_t i;
#pragma omp parallel for
        for (i = i_start; i < i_end; ++i) {
          int64_t v0 = get_v0_from_edge(&buf[i]);
          int64_t v1 = get_v1_from_edge(&buf[i]);
          edge_endpoint[(i - i_start) * 2 + 0] = v0;
          edge_endpoint[(i - i_start) * 2 + 1] = v1;
        }
        get_vertex_distribution_for_pred(2 * (i_end - i_start), edge_endpoint, edge_owner, edge_local);
        begin_gather(pred_win);
#pragma omp parallel for
        for (i = i_start; i < i_end; ++i) {
          add_gather_request(pred_win, (i - i_start) * 2 + 0, edge_owner[(i - i_start) * 2 + 0], edge_local[(i - i_start) * 2 + 0], (i - i_start) * 2 + 0);
          add_gather_request(pred_win, (i - i_start) * 2 + 1, edge_owner[(i - i_start) * 2 + 1], edge_local[(i - i_start) * 2 + 1], (i - i_start) * 2 + 1);
        }
        end_gather(pred_win);
        begin_scatter_constant(pred_valid_win);
#pragma omp parallel for reduction(&&:validation_passed) reduction(+:edge_visit_count)
        for (i = i_start; i < i_end; ++i) {
          int64_t src = get_v0_from_edge(&buf[i]);
          int64_t tgt = get_v1_from_edge(&buf[i]);
          uint16_t src_depth = get_depth_from_pred_entry(edge_preds[(i - i_start) * 2 + 0]);
          uint16_t tgt_depth = get_depth_from_pred_entry(edge_preds[(i - i_start) * 2 + 1]);
          if (src_depth != UINT16_MAX && tgt_depth == UINT16_MAX) {
            fprintf(stderr, "%d: Validation error: edge connects vertex %" PRId64 " in the BFS tree (depth %" PRIu16 ") to vertex %" PRId64 " outside the tree.\n", rank, src, src_depth, tgt);
            validation_passed = 0;
          } else if (src_depth == UINT16_MAX && tgt_depth != UINT16_MAX) {
            fprintf(stderr, "%d: Validation error: edge connects vertex %" PRId64 " in the BFS tree (depth %" PRIu16 ") to vertex %" PRId64 " outside the tree.\n", rank, tgt, tgt_depth, src);
            validation_passed = 0;
          } else if (src_depth - tgt_depth < -1 ||
                     src_depth - tgt_depth > 1) {
            fprintf(stderr, "%d: Validation error: depths of edge endpoints %" PRId64 " (depth %" PRIu16 ") and %" PRId64 " (depth %" PRIu16 ") are too far apart (abs. val. > 1).\n", rank, src, src_depth, tgt, tgt_depth);
            validation_passed = 0;
          } else if (src_depth != UINT16_MAX) {
            ++edge_visit_count;
          }
          if (get_pred_from_pred_entry(edge_preds[(i - i_start) * 2 + 0]) == tgt) {
            add_scatter_constant_request(pred_valid_win, edge_owner[(i - i_start) * 2 + 0], edge_local[(i - i_start) * 2 + 0], (i - i_start) * 2 + 0);
          }
          if (get_pred_from_pred_entry(edge_preds[(i - i_start) * 2 + 1]) == src) {
            add_scatter_constant_request(pred_valid_win, edge_owner[(i - i_start) * 2 + 1], edge_local[(i - i_start) * 2 + 1], (i - i_start) * 2 + 1);
          }
        }
        end_scatter_constant(pred_valid_win);
      }
    } ITERATE_TUPLE_GRAPH_END;
    destroy_gather(pred_win);
    MPI_Free_mem(edge_preds);
    free(edge_owner);
    free(edge_local);
    free(edge_endpoint);
    destroy_scatter_constant(pred_valid_win);
    ptrdiff_t i;
#pragma omp parallel for reduction(&&:validation_passed)
    for (i = 0; i < (ptrdiff_t)nlocalverts; ++i) {
      int64_t p = get_pred_from_pred_entry(pred[i]);
      if (p == -1) continue;
      int found_pred_edge = pred_valid[i];
      if (root_owner == rank && root_local == (size_t)i) found_pred_edge = 1; /* Root vertex */
      if (!found_pred_edge) {
        int64_t v = vertex_to_global_for_pred(rank, i);
        fprintf(stderr, "%d: Validation error: no graph edge from vertex %" PRId64 " to its parent %" PRId64 ".\n", rank, v, get_pred_from_pred_entry(pred[i]));
        validation_passed = 0;
      }
    }
    MPI_Free_mem(pred_valid);

    MPI_Allreduce(MPI_IN_PLACE, &edge_visit_count, 1, MPI_INT64_T, MPI_SUM, MPI_COMM_WORLD);
    *edge_visit_count_ptr = edge_visit_count;
  }
Esempio n. 15
0
/* Check the BFS levels in pred against the predecessors given there.  Returns
 * true if the maps are valid. */
static int check_bfs_depth_map_using_predecessors(const tuple_graph* const tg, const int64_t nglobalverts, const size_t nlocalverts, const size_t maxlocalverts, const int64_t root, const int64_t* const pred) {
  (void)nglobalverts; /* Avoid warning */
  assert (tg->edgememory_size >= 0 && tg->max_edgememory_size >= tg->edgememory_size && tg->max_edgememory_size <= tg->nglobaledges);
  assert (root >= 0 && root < nglobalverts);
  assert (nglobalverts >= 0);
  assert (pred);

  int validation_passed = 1;
  int root_owner;
  size_t root_local;
  get_vertex_distribution_for_pred(1, &root, &root_owner, &root_local);
  int root_is_mine = (root_owner == rank);
  if (root_is_mine) assert (root_local < nlocalverts);

  {
    ptrdiff_t i;
    if (root_is_mine && get_depth_from_pred_entry(pred[root_local]) != 0) {
      fprintf(stderr, "%d: Validation error: depth of root vertex %" PRId64 " is %" PRIu16 ", not 0.\n", rank, root, get_depth_from_pred_entry(pred[root_local]));
      validation_passed = 0;
    }
#pragma omp parallel for reduction(&&:validation_passed)
    for (i = 0; i < (ptrdiff_t)nlocalverts; ++i) {
      if (get_pred_from_pred_entry(pred[i]) == -1 &&
          get_depth_from_pred_entry(pred[i]) != UINT16_MAX) {
        fprintf(stderr, "%d: Validation error: depth of vertex %" PRId64 " with no predecessor is %" PRIu16 ", not UINT16_MAX.\n", rank, vertex_to_global_for_pred(rank, i), get_depth_from_pred_entry(pred[i]));
        validation_passed = 0;
      } else if (get_pred_from_pred_entry(pred[i]) != -1 &&
                 get_depth_from_pred_entry(pred[i]) == UINT16_MAX) {
        fprintf(stderr, "%d: Validation error: predecessor of claimed unreachable vertex %" PRId64 " is %" PRId64 ", not -1.\n", rank, vertex_to_global_for_pred(rank, i), get_pred_from_pred_entry(pred[i]));
        validation_passed = 0;
      }
    }
  }
  int64_t* restrict pred_pred = (int64_t*)xMPI_Alloc_mem(size_min(CHUNKSIZE, nlocalverts) * sizeof(int64_t)); /* Predecessor info of predecessor vertex for each local vertex */
  gather* pred_win = init_gather((void*)pred, nlocalverts, sizeof(int64_t), pred_pred, size_min(CHUNKSIZE, nlocalverts), size_min(CHUNKSIZE, nlocalverts), MPI_INT64_T);
  int64_t* restrict pred_vtx = (int64_t*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(int64_t)); /* Vertex (not depth) part of pred map */
  int* restrict pred_owner = (int*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(int));
  size_t* restrict pred_local = (size_t*)xmalloc(size_min(CHUNKSIZE, nlocalverts) * sizeof(size_t));
  size_t ii;
  for (ii = 0; ii < maxlocalverts; ii += CHUNKSIZE) {
    ptrdiff_t i_start = ptrdiff_min(ii, nlocalverts);
    ptrdiff_t i_end = ptrdiff_min(ii + CHUNKSIZE, nlocalverts);
    begin_gather(pred_win);
    ptrdiff_t i;
    assert (i_start >= 0 && i_start <= (ptrdiff_t)nlocalverts);
    assert (i_end >= 0 && i_end <= (ptrdiff_t)nlocalverts);
    assert (i_end >= i_start);
    assert (i_end - i_start >= 0 && i_end - i_start <= (ptrdiff_t)size_min(CHUNKSIZE, nlocalverts));
#pragma omp parallel for
    for (i = i_start; i < i_end; ++i) {
      pred_vtx[i - i_start] = get_pred_from_pred_entry(pred[i]);
    }
    get_vertex_distribution_for_pred(i_end - i_start, pred_vtx, pred_owner, pred_local);
#pragma omp parallel for
    for (i = i_start; i < i_end; ++i) {
      if (pred[i] != -1) {
        add_gather_request(pred_win, i - i_start, pred_owner[i - i_start], pred_local[i - i_start], i - i_start);
      } else {
        pred_pred[i - i_start] = -1;
      }
    }
    end_gather(pred_win);
#pragma omp parallel for reduction(&&:validation_passed)
    for (i = i_start; i < i_end; ++i) {
      if (rank == root_owner && (size_t)i == root_local) continue;
      if (get_pred_from_pred_entry(pred[i]) == -1) continue; /* Already checked */
      if (get_depth_from_pred_entry(pred_pred[i - i_start]) == UINT16_MAX) {
        fprintf(stderr, "%d: Validation error: predecessor %" PRId64 " of vertex %" PRId64 " (depth %" PRIu16 ") is marked as unreachable.\n", rank, get_pred_from_pred_entry(pred[i]), vertex_to_global_for_pred(rank, i), get_depth_from_pred_entry(pred[i]));
        validation_passed = 0;
      }
      if (get_depth_from_pred_entry(pred[i]) != get_depth_from_pred_entry(pred_pred[i - i_start]) + 1) {
        fprintf(stderr, "%d: Validation error: BFS predecessors do not form a tree; see vertices %" PRId64 " (depth %" PRIu16 ") and %" PRId64 " (depth %" PRIu16 ").\n", rank, vertex_to_global_for_pred(rank, i), get_depth_from_pred_entry(pred[i]), get_pred_from_pred_entry(pred[i]), get_depth_from_pred_entry(pred_pred[i - i_start]));
        validation_passed = 0;
      }
    }
  }
  destroy_gather(pred_win);
  MPI_Free_mem(pred_pred);
  free(pred_owner);
  free(pred_local);
  free(pred_vtx);
  return validation_passed;
}
/*
 * Receive a message from channel.  On success it returns the number of
 * bytes read; otherwise, returns -1.
 */
ssize_t NaClSrpcMessageChannelReceive(struct NaClSrpcMessageChannel* channel,
                                      NaClSrpcMessageHeader* header) {
  /*
   * TODO(sehr): A large prefix of this function is common with Peek.
   * Find a way to merge them.
   */
  ssize_t imc_ret = -1;
  NaClSrpcMessageHeader header_copy;
  struct NaClImcMsgIoVec* iovec = NULL;
  LengthHeader total_size;
  LengthHeader fragment_size;
  LengthHeader processed_size;
  size_t bytes_received;
  size_t descs_received;
  ssize_t retval = -NACL_ABI_EINVAL;

  NaClSrpcLog(3, "NaClSrpcMessageChannelReceive: waiting for message.\n");
  /*
   * The first fragment consists of two LengthHeaders and a fraction of the
   * bytes (starting at 0) and the fraction of descs (starting at 0).
   */
  iovec = CopyAndAddIovs(header->iov, header->iov_length, 2);
  if (NULL == iovec) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive: CopyAndAddIovs failed.\n");
    goto done;
  }
  header_copy.iov = iovec;
  header_copy.iov_length = header->iov_length + 2;
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESCV =
      header->NACL_SRPC_MESSAGE_HEADER_DESCV;
  /* SRPC_DESC_MAX <= NACL_ABI_SIZE_T_MAX, so the cast is safe. */
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = (nacl_abi_size_t)
      size_min(SRPC_DESC_MAX, header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH);
  header_copy.iov[0].base = &total_size;
  header_copy.iov[0].length = sizeof total_size;
  header_copy.iov[1].base = &fragment_size;
  header_copy.iov[1].length = sizeof fragment_size;
  header_copy.flags = 0;
  if (-1 == HeaderTotalBytes(&header_copy, 0)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive: header size overflow.\n");
    goto done;
  }
  /*
   * The message receive should return at least
   * kFragmentOverhead[FIRST_FRAGMENT] bytes.
   */
  imc_ret = MessageChannelBufferRead(channel, &header_copy, 0);
  if (imc_ret < (ssize_t) kFragmentOverhead[FIRST_FRAGMENT]) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive: read failed (%"NACL_PRIdS").\n",
                imc_ret);
    retval = ErrnoFromImcRet(imc_ret);
    goto done;
  }
  /* Comparison above guarantees no underflow. */
  bytes_received = imc_ret - kFragmentOverhead[FIRST_FRAGMENT];
  descs_received = header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
  if (!MessageLengthsAreSane(
          &total_size,
          &fragment_size,
          (size_t) imc_ret,
          header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelReceive:"
                " first fragment descriptor check failed.\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelReceive:"
              " new message, bytes %"NACL_PRIdNACL_SIZE
              ", descs %"NACL_PRIdNACL_SIZE".\n",
              total_size.byte_count,
              total_size.desc_count);
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelReceive:"
              " first fragment, bytes %"NACL_PRIdNACL_SIZE
              ", descs %"NACL_PRIdNACL_SIZE".\n",
              fragment_size.byte_count,
              fragment_size.desc_count);
  processed_size = fragment_size;
  ConsumeFragment(&header_copy, &fragment_size, 2);
  /*
   * Get the remaining fragments.
   */
  while (processed_size.byte_count < total_size.byte_count ||
         processed_size.desc_count < total_size.desc_count) {
    /*
     * The non-first fragments consist of a single LengthHeader and a
     * portion of the remaining iov entries and descv entries.  We add the
     * fragment length descriptor to the preceding iov entry, which is safe,
     * because we know that ConsumeFragment always consumes at least the
     * fragment length descriptor from last time.
     */
    header_copy.iov = header_copy.iov - 1;
    header_copy.iov_length = header_copy.iov_length + 1;
    header_copy.iov[0].base = &fragment_size;
    header_copy.iov[0].length = sizeof fragment_size;
    header_copy.NACL_SRPC_MESSAGE_HEADER_DESCV =
        header->NACL_SRPC_MESSAGE_HEADER_DESCV + descs_received;
    header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = (nacl_abi_size_t)
        size_min(SRPC_DESC_MAX,
                 (header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH -
                  descs_received));
    if (-1 == HeaderTotalBytes(&header_copy, 0)) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelReceive: header size overflow.\n");
      goto done;
    }
    /*
     * The message receive should return at least
     * kFragmentOverhead[LATER_FRAGMENT] bytes.  This is needed to make sure
     * that we can correctly maintain the index into bytes and descs.
     */
    imc_ret = ImcRecvmsg(channel->desc.raw_desc, &header_copy, 0);
    if (imc_ret < (ssize_t) kFragmentOverhead[LATER_FRAGMENT]) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelReceive: read failed (%"
                  NACL_PRIdS").\n",
                  imc_ret);
      retval = ErrnoFromImcRet(imc_ret);
      goto done;
    }
    /* Comparison above guarantees no underflow. */
    bytes_received += imc_ret - kFragmentOverhead[LATER_FRAGMENT];
    descs_received += header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
    if (!FragmentLengthIsSane(
            &fragment_size,
            (size_t) imc_ret,
            header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "NaClSrpcMessageChannelReceive:"
                  " other fragment descriptor check failed.\n");
      retval = -NACL_ABI_EIO;
      goto done;
    }
    NaClSrpcLog(3,
                "NaClSrpcMessageChannelReceive:"
                " next fragment, bytes %"NACL_PRIdNACL_SIZE
                ", descs %"NACL_PRIdNACL_SIZE".\n",
                fragment_size.byte_count,
                fragment_size.desc_count);
    processed_size.byte_count += fragment_size.byte_count;
    processed_size.desc_count += fragment_size.desc_count;
    ConsumeFragment(&header_copy, &fragment_size, 1);
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelReceive:"
              " succeeded, read %"NACL_PRIdS" bytes and %"
              NACL_PRIdNACL_SIZE" descs.\n",
              bytes_received,
              processed_size.desc_count);
  retval = (ssize_t) bytes_received;
  header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH =
      (nacl_abi_size_t) descs_received;
  header->flags = header_copy.flags;

 done:
  free(iovec);
  return retval;
}
/*
 * Peek a message from channel.  Reads the first fragment of the message and
 * leaves it available for future calls to Peek or Receive.
 */
ssize_t NaClSrpcMessageChannelPeek(struct NaClSrpcMessageChannel* channel,
                                   NaClSrpcMessageHeader* header) {
  /*
   * TODO(sehr): Most of this function is common with Receive.
   * Find a way to merge them.
   */
  struct NaClImcMsgIoVec* iovec = NULL;
  NaClSrpcMessageHeader header_copy;
  LengthHeader total_size;
  LengthHeader fragment_size;
  ssize_t imc_ret;
  ssize_t retval = -NACL_ABI_EINVAL;

  /* Append the fragment headers to the iov. */
  iovec = CopyAndAddIovs(header->iov, header->iov_length, 2);
  if (NULL == iovec) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelPeek: CopyAndAddIovs failed.\n");
    return -1;
  }
  header_copy.iov = iovec;
  header_copy.iov_length = header->iov_length + 2;
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESCV =
      header->NACL_SRPC_MESSAGE_HEADER_DESCV;
  /* SRPC_DESC_MAX <= NACL_ABI_SIZE_T_MAX, so the cast is safe. */
  header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = (nacl_abi_size_t)
      size_min(SRPC_DESC_MAX, header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH);
  header_copy.iov[0].base = &total_size;
  header_copy.iov[0].length = sizeof total_size;
  header_copy.iov[1].base = &fragment_size;
  header_copy.iov[1].length = sizeof fragment_size;
  header_copy.flags = 0;
  if (-1 == HeaderTotalBytes(&header_copy, 0)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelPeek: header size overflow.\n");
    goto done;
  }
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelPeek: read message bytes %"
              NACL_PRIdS", descs %"NACL_PRIdS".\n",
              channel->byte_count,
              channel->desc_count);
  imc_ret = MessageChannelBufferRead(channel, &header_copy, 1);
  if (imc_ret < (ssize_t) kFragmentOverhead[FIRST_FRAGMENT]) {
    NaClSrpcLog(3,
                "NaClSrpcMessageChannelPeek: read failed (%"NACL_PRIdS").\n",
                imc_ret);
    retval = ErrnoFromImcRet(imc_ret);
    goto done;
  }
  header->flags = header_copy.flags;
  header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH =
      header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH;
  NaClSrpcLog(3,
              "NaClSrpcMessageChannelPeek: flags %x.\n",
              header->flags);
  if (!MessageLengthsAreSane(
           &total_size,
           &fragment_size,
           (size_t) imc_ret,
           header_copy.NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH)) {
    NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                "NaClSrpcMessageChannelPeek: message length mismatch.\n");
    retval = -NACL_ABI_EIO;
    goto done;
  }
  /* Comparison above guarantees no underflow. */
  retval = imc_ret - kFragmentOverhead[FIRST_FRAGMENT];

 done:
  free(iovec);
  return retval;
}
/*
 * Read from channel's buffer.
 */
static ssize_t MessageChannelBufferRead(struct NaClSrpcMessageChannel* channel,
                                        NaClSrpcMessageHeader* header,
                                        int peeking) {
  size_t i;
  size_t byte_count = 0;
  size_t iov_read_size;
  size_t descv_read_count;

  /*
   * If there are no bytes or descriptors in the buffer, fill the buffer
   * by reading the first fragment.
   */
  if (channel->byte_count == 0 && channel->desc_count == 0) {
    if (!peeking) {
      /* A read with an empty buffer just reads. */
      return ImcRecvmsg(channel->desc.raw_desc, header, 0);
    }
    /* Peeking needs to read the first fragment into the buffer. */
    if (!MessageChannelBufferFirstFragment(channel)) {
      NaClSrpcLog(3,
                  "MessageChannelBufferRead: couldn't buffer.\n");
      return -1;
    }
  }
  header->flags = 0;
  NaClSrpcLog(3,
              "MessageChannelBufferRead: channel->byte_count=%"NACL_PRIdS".\n",
              channel->byte_count);
  for (i = 0; i < header->iov_length; ++i) {
    NaClSrpcLog(3,
                "MessageChannelBufferRead: bytes %"NACL_PRIdS" chan %"
                NACL_PRIdS".\n",
                byte_count,
                channel->byte_count);
    if (channel->byte_count < byte_count) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "MessageChannelBufferRead: overflow.\n");
      return -1;
    }
    iov_read_size =
        size_min(channel->byte_count - byte_count, header->iov[i].length);
    if (SIZE_T_MAX - byte_count < iov_read_size) {
      NaClSrpcLog(NACL_SRPC_LOG_ERROR,
                  "MessageChannelBufferRead: overflow.\n");
      return -1;
    }
    memcpy(header->iov[i].base, channel->bytes + byte_count, iov_read_size);
    byte_count += iov_read_size;
    if (byte_count == channel->byte_count) {
      /* We have read the entire contents of the buffer. */
      NaClSrpcLog(3,
                  "MessageChannelBufferRead: break\n");
      break;
    }
  }
  if (byte_count < channel->byte_count) {
    header->flags |= NACL_ABI_RECVMSG_DATA_TRUNCATED;
  }
  descv_read_count =
      size_min(channel->desc_count,
               header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH);
  /* channel->desc_count <= NACL_ABI_SIZE_T_MAX, so casts are safe. */
  if (SIZE_T_MAX / sizeof(NaClSrpcMessageDesc) < descv_read_count) {
    /* Descriptor descv_read_count * sizeof would overflow. */
    return -1;
  }
  memcpy(header->NACL_SRPC_MESSAGE_HEADER_DESCV,
         channel->descs,
         descv_read_count * sizeof(NaClSrpcMessageDesc));
  header->NACL_SRPC_MESSAGE_HEADER_DESC_LENGTH = (nacl_abi_size_t)
      descv_read_count;
  if (descv_read_count < channel->desc_count) {
    header->flags |= NACL_ABI_RECVMSG_DESC_TRUNCATED;
  }
  /* Reading clears the fragment from the buffer. */
  if (!peeking) {
    channel->byte_count = 0;
    channel->desc_count = 0;
  }
  return (ssize_t) byte_count;
}