Пример #1
0
static ssize_t
ofi_nd_ep_readv(struct fid_ep *pep, const struct iovec *iov, void **desc,
		size_t count, fi_addr_t src_addr, uint64_t addr, uint64_t key,
		void *context)
{
	struct fi_rma_iov rma_iov = {
		.addr = addr,
		.len = iov[0].iov_len,
		.key = key
	};

	struct fi_msg_rma msg = {
		.msg_iov = iov,
		.desc = desc,
		.iov_count = count,
		.addr = src_addr,
		.rma_iov = &rma_iov,
		.rma_iov_count = 1,
		.context = context,
		.data = 0
	};

	assert(pep->fid.fclass == FI_CLASS_EP);

	if (pep->fid.fclass != FI_CLASS_EP)
		return -FI_EINVAL;

	struct nd_ep *ep = container_of(pep, struct nd_ep, fid);

	return ofi_nd_ep_readmsg(pep, &msg, ep->info->rx_attr->op_flags);
}

static ssize_t
ofi_nd_ep_readmsg(struct fid_ep *pep, const struct fi_msg_rma *msg,
		  uint64_t flags)
{
	assert(pep->fid.fclass == FI_CLASS_EP);
	assert(msg);

	if (pep->fid.fclass != FI_CLASS_EP)
		return -FI_EINVAL;

	size_t msg_len = 0, rma_len = 0, i;
	HRESULT hr;

	struct nd_ep *ep = container_of(pep, struct nd_ep, fid);

	if (!ep->qp)
		return -FI_EOPBADSTATE;

	for (i = 0; i < msg->iov_count; i++) {
		if (msg->msg_iov[i].iov_len && !msg->msg_iov[i].iov_base)
			return -FI_EINVAL;
		msg_len += msg->msg_iov[i].iov_len;
	}

	for (i = 0; i < msg->rma_iov_count; i++) {
		if (msg->rma_iov[i].len && !msg->rma_iov[i].addr)
			return -FI_EINVAL;
		rma_len += msg->rma_iov[i].len;
	}

	/* Check the following: */
	if ((msg_len != rma_len) || /* - msg and rma len are correlated */
	    /* - iov counts are less or equal than supported */
	    (msg->iov_count > ND_MSG_IOV_LIMIT ||
	     msg->rma_iov_count > ND_MSG_IOV_LIMIT) ||
	    /* - transmitted length is less or equal than max possible */
	    (msg_len > ep->domain->info->ep_attr->max_msg_size))
		return -FI_EINVAL;

	struct nd_cq_entry *main_entry = ofi_nd_buf_alloc_nd_cq_entry();
	if (!main_entry)
		return -FI_ENOMEM;
	memset(main_entry, 0, sizeof(*main_entry));
	main_entry->data = msg->data;
	main_entry->flags = flags;
	main_entry->domain = ep->domain;
	main_entry->context = msg->context;
	main_entry->seq = InterlockedAdd64(&ep->domain->msg_cnt, 1);

	/* since write operation can't be canceled, set NULL into
	 * the 1st byte of internal data of context */
	if (msg->context)
		ND_FI_CONTEXT(msg->context) = 0;

	struct fi_rma_iov rma_iovecs[ND_MSG_INTERNAL_IOV_LIMIT];
	size_t rma_count = 0;
	size_t from_split_map[ND_MSG_INTERNAL_IOV_LIMIT];
	size_t to_split_map[ND_MSG_INTERNAL_IOV_LIMIT];
	uint64_t remote_addr[ND_MSG_INTERNAL_IOV_LIMIT];

	ofi_nd_split_msg_iov_2_rma_iov(msg->rma_iov, msg->rma_iov_count,
				       msg->msg_iov, msg->iov_count,
				       rma_iovecs, &rma_count,
				       from_split_map, to_split_map, remote_addr);

	assert(rma_count <= ND_MSG_INTERNAL_IOV_LIMIT);

	main_entry->wait_completion.comp_count = 0;
	main_entry->wait_completion.total_count = rma_count;

	InitializeCriticalSection(&main_entry->wait_completion.comp_lock);

	struct nd_cq_entry *entries[ND_MSG_IOV_LIMIT];

	for (i = 0; i < rma_count; i++) {
		entries[i] = ofi_nd_buf_alloc_nd_cq_entry();
		if (!entries[i])
			goto fn_fail;
		memset(entries[i], 0, sizeof(*entries[i]));

		entries[i]->data = msg->data;
		entries[i]->flags = flags;
		entries[i]->domain = ep->domain;
		entries[i]->context = msg->context;
		entries[i]->seq = main_entry->seq;
		entries[i]->aux_entry = main_entry;

		hr = ep->domain->adapter->lpVtbl->CreateMemoryRegion(
			ep->domain->adapter, &IID_IND2MemoryRegion,
			ep->domain->adapter_file, (void**)&entries[i]->mr[0]);
		if (FAILED(hr))
			goto fn_fail;
		entries[i]->mr_count = 1;

		hr = ofi_nd_util_register_mr(
			entries[i]->mr[0],
			(const void *)remote_addr[i],
			rma_iovecs[i].len,
			ND_MR_FLAG_ALLOW_LOCAL_WRITE |
			ND_MR_FLAG_ALLOW_REMOTE_READ |
			ND_MR_FLAG_ALLOW_REMOTE_WRITE);
		if (FAILED(hr))
			goto fn_fail;

		ND2_SGE sge = {
			.Buffer = (void *)remote_addr[i],
			.BufferLength = (ULONG)rma_iovecs[i].len,
			.MemoryRegionToken = (UINT32)(uintptr_t)msg->desc[to_split_map[i]]
		};

		hr = ep->qp->lpVtbl->Read(ep->qp, entries[i], &sge, 1,
			(UINT64)rma_iovecs[i].addr, (UINT32)rma_iovecs[i].key, 0);
		if (FAILED(hr))
			goto fn_fail;
	}

	return FI_SUCCESS;

fn_fail:
	while (i-- > 0)
		ofi_nd_free_cq_entry(entries[i]);
	ND_LOG_WARN(FI_LOG_EP_DATA, ofi_nd_strerror((DWORD)hr, NULL));
	return H2F(hr);
}

static ssize_t
ofi_nd_ep_write(struct fid_ep *ep, const void *buf, size_t len, void *desc,
		fi_addr_t dest_addr, uint64_t addr, uint64_t key, void *context)
{
	struct iovec iov = {
		.iov_base = (void*)buf,
		.iov_len = len
	};
	return ofi_nd_ep_writev(ep, &iov, &desc, 1, dest_addr, addr, key, context);
}

static ssize_t
ofi_nd_ep_writev(struct fid_ep *pep, const struct iovec *iov, void **desc,
		 size_t count, fi_addr_t dest_addr, uint64_t addr, uint64_t key,
		 void *context)
{
	struct fi_rma_iov rma_iov = {
		.addr = addr,
		.len = iov[0].iov_len,
		.key = key
	};

	struct fi_msg_rma msg = {
		.msg_iov = iov,
		.desc = desc,
		.iov_count = count,
		.addr = dest_addr,
		.rma_iov = &rma_iov,
		.rma_iov_count = 1,
		.context = context,
		.data = 0
	};

	assert(pep->fid.fclass == FI_CLASS_EP);

	if (pep->fid.fclass != FI_CLASS_EP)
		return -FI_EINVAL;

	struct nd_ep *ep = container_of(pep, struct nd_ep, fid);

	return ofi_nd_ep_writemsg(pep, &msg, ep->info->tx_attr->op_flags);
}

static ssize_t
ofi_nd_ep_writemsg(struct fid_ep *pep, const struct fi_msg_rma *msg,
		   uint64_t flags)
{
	assert(pep->fid.fclass == FI_CLASS_EP);
	assert(msg);

	if (pep->fid.fclass != FI_CLASS_EP)
		return -FI_EINVAL;

	size_t msg_len = 0, rma_len = 0, i;
	HRESULT hr;

	struct nd_cq_entry *entries[ND_MSG_IOV_LIMIT];
	struct nd_ep *ep = container_of(pep, struct nd_ep, fid);

	if (!ep->qp)
		return -FI_EOPBADSTATE;

	for (i = 0; i < msg->iov_count; i++) {
		if (msg->msg_iov[i].iov_len && !msg->msg_iov[i].iov_base)
			return -FI_EINVAL;
		msg_len += msg->msg_iov[i].iov_len;
	}

	if ((msg_len > ep->domain->info->ep_attr->max_msg_size) &&
	    (flags & FI_INJECT))
		return -FI_EINVAL;

	for (i = 0; i < msg->rma_iov_count; i++) {
		if (msg->rma_iov[i].len && !msg->rma_iov[i].addr) 
			return -FI_EINVAL;
		rma_len += msg->rma_iov[i].len;
	}

	/* Check the following: */
	if ((msg_len != rma_len) || /* - msg and rma len are correlated */
	    /* - iov counts are less or equal than supported */
	    ((msg->iov_count > ND_MSG_IOV_LIMIT ||
	      msg->rma_iov_count > ND_MSG_IOV_LIMIT)) ||
	    /* - transmitted length is less or equal than max possible */
	    (msg_len > ep->domain->info->ep_attr->max_msg_size) ||
	    /* - if INJECT, data should be inlined */
	    ((flags & FI_INJECT) &&
	     (msg_len > ep->domain->info->tx_attr->inject_size)))
		return -FI_EINVAL;

	struct nd_cq_entry *main_entry = ofi_nd_buf_alloc_nd_cq_entry();
	if (!main_entry)
		return -FI_ENOMEM;
	memset(main_entry, 0, sizeof(*main_entry));
	main_entry->data = msg->data;
	main_entry->flags = flags;
	main_entry->domain = ep->domain;
	main_entry->context = msg->context;
	main_entry->seq = InterlockedAdd64(&ep->domain->msg_cnt, 1);

	/* since write operation can't be canceled, set NULL into
	* the 1st byte of internal data of context */
	if (msg->context)
		ND_FI_CONTEXT(msg->context) = 0;

	/* TODO */
	if (msg_len > (size_t)gl_data.inline_thr) {
		struct fi_rma_iov rma_iovecs[ND_MSG_INTERNAL_IOV_LIMIT];
		size_t rma_count = 0;
		size_t from_split_map[ND_MSG_INTERNAL_IOV_LIMIT];
		size_t to_split_map[ND_MSG_INTERNAL_IOV_LIMIT];
		uint64_t remote_addr[ND_MSG_INTERNAL_IOV_LIMIT];

		ofi_nd_split_msg_iov_2_rma_iov(msg->rma_iov, msg->rma_iov_count,
					       msg->msg_iov, msg->iov_count,
					       rma_iovecs, &rma_count,
					       from_split_map, to_split_map, remote_addr);

		assert(rma_count <= ND_MSG_INTERNAL_IOV_LIMIT);

		main_entry->wait_completion.comp_count = 0;
		main_entry->wait_completion.total_count = rma_count;

		InitializeCriticalSection(&main_entry->wait_completion.comp_lock);

		for (i = 0; i < rma_count; i++) {
			entries[i] = ofi_nd_buf_alloc_nd_cq_entry();
			if (!entries[i])
				goto fn_fail;
			memset(entries[i], 0, sizeof(*entries[i]));

			entries[i]->data = msg->data;
			entries[i]->flags = flags;
			entries[i]->domain = ep->domain;
			entries[i]->context = msg->context;
			entries[i]->seq = main_entry->seq;
			entries[i]->aux_entry = main_entry;

			ND2_SGE sge = {
				.Buffer = (void *)remote_addr[i],
				.BufferLength = (ULONG)rma_iovecs[i].len,
				.MemoryRegionToken = (UINT32)(uintptr_t)msg->desc[to_split_map[i]]
			};

			hr = ep->qp->lpVtbl->Write(ep->qp, entries[i], &sge, 1,
				(UINT64)rma_iovecs[i].addr, (UINT32)rma_iovecs[i].key, 0);
			if (FAILED(hr))
				goto fn_fail;
		}

		return FI_SUCCESS;
	}
	else {
		if (msg_len) {
			main_entry->inline_buf = __ofi_nd_buf_alloc_nd_inlinebuf(&ep->domain->inlinebuf);
			if (!main_entry->inline_buf)
				return -FI_ENOMEM;

			char *buf = (char*)main_entry->inline_buf->buffer;
			for (i = 0; i < msg->iov_count; i++) {
				memcpy(buf, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
				buf += msg->msg_iov[i].iov_len;
			}
		}

		for (i = 0; i < msg->rma_iov_count; i++) {
			char *buf = (char *)main_entry->inline_buf->buffer;

			entries[i] = ofi_nd_buf_alloc_nd_cq_entry();
			if (!entries[i])
				goto fn_fail;
			memset(entries[i], 0, sizeof(*entries[i]));

			entries[i]->data = msg->data;
			entries[i]->flags = flags;
			entries[i]->domain = ep->domain;
			entries[i]->context = msg->context;
			entries[i]->seq = main_entry->seq;
			entries[i]->aux_entry = main_entry;

			ND2_SGE sge = {
				.Buffer = (void *)(buf + msg->rma_iov[i].len),
				.BufferLength = (ULONG)msg->rma_iov[i].len,
				.MemoryRegionToken = main_entry->inline_buf->token
			};

			hr = ep->qp->lpVtbl->Write(ep->qp, entries[i], &sge, 1,
				(UINT64)msg->rma_iov[i].addr, (UINT32)msg->rma_iov[i].key, 0);
			if (FAILED(hr))
				goto fn_fail;
		}

		return FI_SUCCESS;
	}
fn_fail:
	while (i-- > 0)
		ofi_nd_free_cq_entry(entries[i]);
	ND_LOG_WARN(FI_LOG_EP_DATA, ofi_nd_strerror((DWORD)hr, NULL));
	return H2F(hr);
}

static ssize_t
ofi_nd_ep_inject(struct fid_ep *pep, const void *buf, size_t len,
		 fi_addr_t dest_addr, uint64_t addr, uint64_t key)
{
	struct iovec iov = {
		.iov_base = (void*)buf,
		.iov_len = len
	};

	struct fi_rma_iov rma_iov = {
		.addr = addr,
		.len = len,
		.key = key
	};

	struct fi_msg_rma msg = {
		.msg_iov = &iov,
		.desc = 0,
		.iov_count = 1,
		.addr = dest_addr,
		.rma_iov = &rma_iov,
		.rma_iov_count = 1,
		.context = 0,
		.data = 0
	};

	return ofi_nd_ep_writemsg(pep, &msg, FI_INJECT);
}

static ssize_t
ofi_nd_ep_writedata(struct fid_ep *pep, const void *buf, size_t len, void *desc,
		    uint64_t data, fi_addr_t dest_addr, uint64_t addr, uint64_t key,
		    void *context)
{
	struct iovec iov = {
		.iov_base = (void*)buf,
		.iov_len = len
	};

	struct fi_rma_iov rma_iov = {
		.addr = addr,
		.len = len,
		.key = key
	};

	struct fi_msg_rma msg = {
		.msg_iov = &iov,
		.desc = &desc,
		.iov_count = 1,
		.addr = dest_addr,
		.rma_iov = &rma_iov,
		.rma_iov_count = 1,
		.context = context,
		.data = data
	};

	assert(pep->fid.fclass == FI_CLASS_EP);

	if (pep->fid.fclass != FI_CLASS_EP)
		return -FI_EINVAL;

	struct nd_ep *ep = container_of(pep, struct nd_ep, fid);

	return ofi_nd_ep_writemsg(pep, &msg, ep->info->tx_attr->op_flags | FI_REMOTE_CQ_DATA);
}

static ssize_t
ofi_nd_ep_writeinjectdata(struct fid_ep *ep, const void *buf, size_t len,
			  uint64_t data, fi_addr_t dest_addr, uint64_t addr,
			  uint64_t key)
{
	struct iovec iov = {
		.iov_base = (void*)buf,
		.iov_len = len
	};

	struct fi_rma_iov rma_iov = {
		.addr = addr,
		.len = len,
		.key = key
	};

	struct fi_msg_rma msg = {
		.msg_iov = &iov,
		.desc = 0,
		.iov_count = 1,
		.addr = dest_addr,
		.rma_iov = &rma_iov,
		.rma_iov_count = 1,
		.context = 0,
		.data = data
	};

	return ofi_nd_ep_writemsg(ep, &msg, FI_INJECT | FI_REMOTE_CQ_DATA);
}

void ofi_nd_read_event(ND2_RESULT *result)
{
	assert(result);
	assert(result->RequestType == Nd2RequestTypeRead);

	nd_cq_entry *entry = (nd_cq_entry*)result->RequestContext;
	assert(entry);

	ND_LOG_EVENT_INFO(entry);

	/* Check whether the operation is complex, i.e. read operation
	 * may consists from several subtasks of read */
	if (entry->aux_entry) {
		EnterCriticalSection(&entry->aux_entry->wait_completion.comp_lock);
		entry->aux_entry->wait_completion.comp_count++;
		ND_LOG_DEBUG(FI_LOG_EP_DATA, "READ Event comp_count = %d, total_count = %d\n",
			entry->aux_entry->wait_completion.comp_count,
			entry->aux_entry->wait_completion.total_count);
		if (entry->aux_entry->wait_completion.comp_count < entry->aux_entry->wait_completion.total_count) {
			/* Should wait some remaining completion events about read operation */
			LeaveCriticalSection(&entry->aux_entry->wait_completion.comp_lock);
			entry->aux_entry = NULL;
			ofi_nd_free_cq_entry(entry);
			return;
		}
		LeaveCriticalSection(&entry->aux_entry->wait_completion.comp_lock);
	}

	/*TODO: Handle erroneous case "result->Status != S_OK" */
	ofi_nd_dispatch_cq_event(entry->state == LARGE_MSG_RECV_REQ ?
		LARGE_MSG_REQ : NORMAL_EVENT, entry, result);
}

void ofi_nd_write_event(ND2_RESULT *result)
{
	assert(result);
	assert(result->RequestType == Nd2RequestTypeWrite);

	nd_cq_entry *entry = (nd_cq_entry*)result->RequestContext;
	assert(entry);

	struct nd_ep *ep = (struct nd_ep*)result->QueuePairContext;
	assert(ep);
	assert(ep->fid.fid.fclass == FI_CLASS_EP);

	ND_LOG_EVENT_INFO(entry);

	/* Check whether the operation is complex, i.e. write operation
	 * may consist from several subtasks of write */
	if (entry->aux_entry) {
		EnterCriticalSection(&entry->aux_entry->wait_completion.comp_lock);
		entry->aux_entry->wait_completion.comp_count++;

		if (entry->aux_entry->wait_completion.comp_count < entry->aux_entry->wait_completion.total_count) {
			/* Should wait some remaining completion events about write operation */
			LeaveCriticalSection(&entry->aux_entry->wait_completion.comp_lock);
			entry->aux_entry = NULL;
			ofi_nd_free_cq_entry(entry);
			return;
		}
		LeaveCriticalSection(&entry->aux_entry->wait_completion.comp_lock);
	}

	if (!entry->context) {
		/* This means that this write was an internal event,
		 * just release it */
		ofi_nd_free_cq_entry(entry);
		return;
	}

	if (entry->flags & FI_REMOTE_CQ_DATA) {
		if (ofi_nd_ep_injectdata(
			&ep->fid, 0, 0, entry->data,
			FI_ADDR_UNSPEC) != FI_SUCCESS)
			ND_LOG_WARN(FI_LOG_CQ, "failed to write-inject");
	}

	if (ep->cntr_write) {
		if (result->Status != S_OK) {
			InterlockedIncrement64(&ep->cntr_write->err);
		}
		InterlockedIncrement64(&ep->cntr_write->counter);
		WakeByAddressAll((void*)&ep->cntr_write->counter);
	}

	int notify = ofi_nd_util_completion_blackmagic(
		ep->info->tx_attr->op_flags, ep->send_flags, entry->flags) ||
		result->Status != S_OK;

	if (notify) {
		PostQueuedCompletionStatus(
			entry->result.Status == S_OK ? ep->cq_send->iocp : ep->cq_send->err,
			0, 0, &entry->base.ov);
		InterlockedIncrement(&ep->cq_send->count);
		WakeByAddressAll((void*)&ep->cq_send->count);
	}
	else { /* if notification is not requested - just free entry */
		ofi_nd_free_cq_entry(entry);
	}
}

void ofi_nd_split_msg_iov_2_rma_iov(const struct fi_rma_iov *rma_iovecs, const size_t rma_count,
				    const struct iovec *msg_iovecs, const size_t msg_count,
				    struct fi_rma_iov res_iovecs[ND_MSG_INTERNAL_IOV_LIMIT],
				    size_t *res_count,
				    size_t from_split_map[ND_MSG_INTERNAL_IOV_LIMIT],
				    size_t to_split_map[ND_MSG_INTERNAL_IOV_LIMIT],
				    uint64_t remote_addr[ND_MSG_INTERNAL_IOV_LIMIT])
{
	size_t i;

	struct iovec from_rma_iovecs[ND_MSG_IOV_LIMIT];
	size_t from_rma_count = rma_count;

	struct iovec res_msg_iovecs[ND_MSG_IOV_LIMIT];
	size_t res_msg_count = 0;


	/* Convert RMA iovecs to MSG iovecs to be able to reuse
	 * them in @ofi_nd_repack_iovecs */
	for (i = 0; i < rma_count; i++) {
		from_rma_iovecs[i].iov_base = (void *)rma_iovecs[i].addr;
		from_rma_iovecs[i].iov_len = rma_iovecs[i].len;
	}

	ofi_nd_repack_iovecs(from_rma_iovecs, from_rma_count,
			     msg_iovecs, msg_count,
			     res_msg_iovecs, &res_msg_count,
			     from_split_map, to_split_map, remote_addr);

	/* Extract MSG iov to RMA iovecs and returns them */
	for (i = 0; i < res_msg_count; i++) {
		res_iovecs[i].addr = remote_addr[i];
		res_iovecs[i].len = res_msg_iovecs[i].iov_len;
		res_iovecs[i].key = rma_iovecs[from_split_map[i]].key;

		remote_addr[i] = (uint64_t)res_msg_iovecs[i].iov_base;
	}

	*res_count = res_msg_count;
}
/// SimplePgx::LoadImage
// Load an image from an already open (binary) PPM or PGM file
// Throw in case the file should be invalid.
void SimplePgx::LoadImage(const char *basename,struct ImgSpecs &specs)
{
  struct ComponentName   *name;
  struct ComponentLayout *layout;
  ULONG w,h;
  UWORD depth = 0;
  char buffer[256 + 4];
  bool embedded = false; // if the header is embedded in the file and the file names are generated.
  bool single   = false;
  File file(basename,"r");
  //
  if (m_pComponent) {
    PostError("Image is already loaded.\n");
  }
  //
  // Read the names, one after another.
  do {
    size_t len;
    if (!embedded) {
      if (fgets(buffer,255,file) == NULL) {
	break;
      }
    }
    //
    // Is this probably not the header file but the first data file?
    if (depth == 0 && buffer[0] == 'P' && (buffer[1] == 'G' || buffer[1] == 'F') && buffer[2] == ' ') {
      const char *dot = strrchr(basename,'.');
      if (dot && !strcmp(dot,".pgx") && dot - 1 > basename && dot < basename + 256) {
	if (dot[-1] == '0') {
	  embedded = true;
	} else {
	  embedded = true;
	  single   = true;
	}
      }
    }
    // If embedded, try to algorithmically determine the next file name by replacing the digits before
    // the basename.
    if (embedded) {
      if (single) {
	if (depth != 0)
	  break;
	strcpy(buffer,basename);
      } else {
	FILE *tmp;
	const char *dot  = strrchr(basename,'.');
	int dotpos = dot - 1 - basename;
	memcpy(buffer,basename,dotpos);
	sprintf(buffer+dotpos,"%d.pgx",depth);
	tmp = fopen(buffer,"rb");
	if (!tmp)
	  break;
	fclose(tmp);
      }
    }
    //
    // Check for a terminating /n and remove it.
    len = strlen(buffer);
    while(len > 0) {
      if (isspace(buffer[len-1])) {
	buffer[--len] = '\0';
      } else break;
    }
    if (len > 0) {
      struct ComponentName **last = &m_pNameList;
      name  = new struct ComponentName(buffer);
      // Yup, there's really a file name left, attach it to the end.
      while(*last) {
	last = &((*last)->m_pNext);
      }
      *last = name;
      depth++;
    }
  } while(true);
  //
  if (ferror(file)) {
    PostError("failure on reading the pgx component list file %s\n",basename);
  }
  //
  // Now parse the headers.
  name = m_pNameList;
  while(name) {
    char *data,*last,*dptr;
    FILE *header;
	size_t len;
    int depth;
    // Copy the name over, and replace the .raw with .h.
    strncpy(buffer,name->m_pName,256);
    if (!embedded) {
      len = strlen(buffer);
      if (len > 4) {
	if (!strcmp(buffer + len - 4,".raw"))
	  buffer[len - 4] = 0;
      }
      strcat(buffer,".h");
    }
    header = fopen(buffer,"r");

    if (header == NULL) {
      PostError("cannot open the pgx header file %s\n",buffer);
    }
    //
    // Read the header into the file. This should be one
    // single stream.
    data = fgets(buffer,255,header);
    fclose(header);
    if (data == NULL) {
      PostError("failed to read the pgx header file.\n");
    }
    //
    // Check whether the file header is fine. We only
    // support big endian PGX files, this is enough for
    // part4 compliance. We could also support
    // little endian files.
    if (!memcmp(buffer,"PF LM ",6)) {
      name->m_bLE        = true;
      specs.LittleEndian = ImgSpecs::Yes;
      name->m_bFloat     = true;
    } else if (!memcmp(buffer,"PF ML ",6)) {
      specs.LittleEndian = ImgSpecs::No;
      name->m_bFloat     = true;
    } else if (!memcmp(buffer,"PG LM ",6)) {
      specs.LittleEndian = ImgSpecs::Yes;
      name->m_bLE        = true;
    } else if (!memcmp(buffer,"PG ML ",6)) {
      specs.LittleEndian = ImgSpecs::No;
    } else {
      PostError("invalid PGX file, PGX identifier %s broken",buffer);
    }
    // The next must be + or -, defining the sign.
    dptr = buffer + 7;
    if (buffer[6] == '+' || buffer[6] == ' ') {
      name->m_bSigned = false;
    } else if (buffer[6] == '-') {
      name->m_bSigned = true;
    } else if (buffer[6] >= '0' && buffer[6] <= '9') {
      // Signedness not indicated, this is the depth. Assume unsigned.
      name->m_bSigned = false;
      dptr--;
    } else {
      PostError("invalid PGX file, PGX signedness %c broken\n",buffer[6]);
    }
    // Get the bit depth of the component.
    depth = strtol(dptr,&last,10);
    // Currently, not more than 16bpp.
    if (last <= dptr || last[0] != ' ' || depth <= 0 || depth > 64) {
      PostError("invalid PGX file, bit depth invalid\n");
    }
    name->m_ucDepth = depth;
    //
    data = last + 1;
    name->m_ulWidth = strtol(data,&last,10);
    if (last <= data || last[0] != ' ') {
      PostError("invalid PGX file, width invalid\n");
    }
    data = last + 1;
    name->m_ulHeight = strtol(data,&last,10);
    if (last <= data || !isspace(last[0])) {
      PostError("invalid PGX file, height invalid\n");
    }
    //
    // All done with this file. Get the next.
    name = name->m_pNext;
  }
  //
  // Find the maximum width, height as base for the
  // subsampling. 
  name = m_pNameList;
  w    = 0;
  h    = 0;
  while(name) {
    if (name->m_ulWidth > w)
      w = name->m_ulWidth;
    if (name->m_ulHeight > h)
      h = name->m_ulHeight;
    //
    name = name->m_pNext;
  }
  //
  // Setup the component list and the subsampling.
  CreateComponents(w,h,depth);
  //
  specs.ASCII      = ImgSpecs::No;
  specs.Palettized = ImgSpecs::No;
  //
  // Fill in the subsampling.
  name   = m_pNameList;
  layout = m_pComponent;
  while(name) {
    ULONG size = name->m_ulWidth * name->m_ulHeight;
    UBYTE bypp = (name->m_ucDepth + 7) >> 3;
    FILE *raw;
    // Special hack for half-float: Store as float.
    if (name->m_ucDepth == 16 && name->m_bFloat)
      bypp = 4;
    //
    layout->m_ucBits          = name->m_ucDepth;
    layout->m_bSigned         = name->m_bSigned;
    layout->m_bFloat          = name->m_bFloat;
    // this is only an approximation. Urgh. We don't know the left edge...
    layout->m_ucSubX          = w / name->m_ulWidth;
    // ditto. Same problem.  
    layout->m_ucSubY          = h / name->m_ulHeight; 
    layout->m_ulWidth         = name->m_ulWidth;
    layout->m_ulHeight        = name->m_ulHeight;
    layout->m_ulBytesPerRow   = bypp * name->m_ulWidth;
    layout->m_ulBytesPerPixel = bypp;
    // allocate memory for this component.
    layout->m_pPtr            = name->m_pData = new UBYTE[name->m_ulWidth * name->m_ulHeight * bypp];
    //
    // Now read the data from the raw file.
    raw  = fopen(name->m_pName,"rb");
    if (raw == NULL) {
      PostError("unable to open the PGX raw data file %s\n",name->m_pName);
    }
    if (embedded) {
      int c;
      // Read off the first line.
      while((c = fgetc(raw)) != -1 && c != '\n'){}
      //
      if (c == -1) {
	fclose(raw);
	PostError("invalid data header in embedded PGX file %s\n",name->m_pName);
      }
    }
    // If we have here single bytes, we can parse off the file completely.
    if (name->m_ucDepth <= 8) {
      if (fread(name->m_pData,1,size,raw) != size) {
	fclose(raw);
	PostError("incomplete PGX data file %s\n",name->m_pName);
      }
    } else if (name->m_ucDepth == 16 && name->m_bFloat) {
      // The hacky case for half-float support.
      // We must read the data byte by byte due to endian issues.
      FLOAT *data = (FLOAT *)name->m_pData;
      while(size) {
	int in1,in2;
	in1 = fgetc(raw);
	in2 = fgetc(raw);
	if (in1 < 0 || in2 < 0) {
	  fclose(raw);
	  PostError("incomplete PGX data file %s\n",name->m_pName);
	}
	if (name->m_bLE) {
	  *data++ = H2F((in2 << 8) | in1); // is little endian
	} else {
	  *data++ = H2F((in1 << 8) | in2); // is big endian
	}
	size--;
      }
    } else if (name->m_ucDepth <= 16) {
      // We must read the data byte by byte due to endian issues.
      UWORD *data = (UWORD *)name->m_pData;
      while(size) {
	int in1,in2;
	in1 = fgetc(raw);
	in2 = fgetc(raw);
	if (in1 < 0 || in2 < 0) {
	  fclose(raw);
	  PostError("incomplete PGX data file %s\n",name->m_pName);
	}
	if (name->m_bLE) {
	  *data++ = (in2 << 8) | in1; // is little endian
	} else {
	  *data++ = (in1 << 8) | in2; // is big endian
	}
	size--;
      }
    } else if (name->m_ucDepth <= 32) { 
      ULONG *data = (ULONG *)name->m_pData;
      while(size) {
	int in1,in2,in3,in4;
	in1 = fgetc(raw);
	in2 = fgetc(raw);
	in3 = fgetc(raw);
	in4 = fgetc(raw);
	if (in1 < 0 || in2 < 0 || in3 < 0 || in4 < 0) {
	  fclose(raw);
	  PostError("incomplete PGX data file %s\n",name->m_pName);
	}
	if (name->m_bLE) {
	  *data++ = (ULONG(in4) << 24) | (ULONG(in3) << 16) | (ULONG(in2) << 8) | ULONG(in1); // is little endian
	} else {
	  *data++ = (ULONG(in1) << 24) | (ULONG(in2) << 16) | (ULONG(in3) << 8) | ULONG(in4); // is big endian
	}
	size--;
      }
    } else if (name->m_ucDepth <= 64) {  
      UQUAD *data = (UQUAD *)name->m_pData;
      while(size) {
	int in1,in2,in3,in4,in5,in6,in7,in8;
	in1 = fgetc(raw);
	in2 = fgetc(raw);
	in3 = fgetc(raw);
	in4 = fgetc(raw);
	in5 = fgetc(raw);
	in6 = fgetc(raw);
	in7 = fgetc(raw);
	in8 = fgetc(raw);
	if (in1 < 0 || in2 < 0 || in3 < 0 || in4 < 0 || in5 < 0 || in6 < 0 || in7 < 0 || in8 < 0) {
	  fclose(raw);
	  PostError("incomplete PGX data file %s\n",name->m_pName);
	}
	if (name->m_bLE) {
	  *data++ = (UQUAD(in8) << 56) | (UQUAD(in7) << 48) | (UQUAD(in6) << 40) | (UQUAD(in5) << 32) |
	    (UQUAD(in4) << 24) | (UQUAD(in3) << 16) | (UQUAD(in2) << 8) | UQUAD(in1); // is little endian
	} else {
	  *data++ = (UQUAD(in1) << 56) | (UQUAD(in2) << 48) | (UQUAD(in3) << 40) | (UQUAD(in4) << 32) |
	    (UQUAD(in5) << 24) | (UQUAD(in6) << 16) | (UQUAD(in7) << 8) | UQUAD(in8); // is big endian
	}
	size--;
      }
    }
    if (ferror(raw)) {
      fclose(raw);
      PostError("unable to read PGX data file %s\n",name->m_pName);
    }
    fclose(raw);
    name = name->m_pNext;
    layout++;
  }
}