int dma_get_cache_alignment(void)
{
        return ia64_max_cacheline_size;
}


static void __init setup_crashkernel(unsigned long total, int *n)
{
	unsigned long long base = 0, size = 0;
	int ret;

	ret = parse_crashkernel(boot_command_line, total,
			&size, &base);
	if (ret == 0 && size > 0) {
		if (!base) {
			sort_regions(rsvd_region, *n);
			base = kdump_find_rsvd_region(size,
					rsvd_region, *n);
		}

		if (!check_crashkernel_memory(base, size)) {
			pr_warning("crashkernel: There would be kdump memory "
				"at %ld GB but this is unusable because it "
				"must\nbe below 4 GB. Change the memory "
				"configuration of the machine.\n",
				(unsigned long)(base >> 30));
			return;
		}

		if (base != ~0UL) {
			printk(KERN_INFO "Reserving %ldMB of memory at %ldMB "
					"for crashkernel (System RAM: %ldMB)\n",
					(unsigned long)(size >> 20),
					(unsigned long)(base >> 20),
					(unsigned long)(total >> 20));
			rsvd_region[*n].start =
				(unsigned long)__va(base);
			rsvd_region[*n].end =
				(unsigned long)__va(base + size);
			(*n)++;
			crashk_res.start = base;
			crashk_res.end = base + size - 1;
		}
	}
	efi_memmap_res.start = ia64_boot_param->efi_memmap;
	efi_memmap_res.end = efi_memmap_res.start +
		ia64_boot_param->efi_memmap_size;
	boot_param_res.start = __pa(ia64_boot_param);
	boot_param_res.end = boot_param_res.start +
		sizeof(*ia64_boot_param);
}


messageGetDispositionType(const message *m)
{
	return (m->mimeDispositionType) ? m->mimeDispositionType : "";
}


messageAddStr(message *m, const char *data)
{
	line_t *repeat = NULL;

	assert(m != NULL);

	if(data) {
		if(*data == '\0')
			data = NULL;
		else {
			/*
			 * If it's only white space, just store one space to
			 * save memory. You must store something since it may
			 * be a header line
			 */
			int iswhite = 1;
			const char *p;

			for(p = data; *p; p++)
				if(((*p) & 0x80) || !isspace(*p)) {
					iswhite = 0;
					break;
				}
			if(iswhite) {
				/*cli_dbgmsg("messageAddStr: empty line: '%s'\n", data);*/
				data = " ";
			}
		}
	}

	if(m->body_first == NULL)
		m->body_last = m->body_first = (text *)cli_malloc(sizeof(text));
	else {
		assert(m->body_last != NULL);
		if((data == NULL) && (m->body_last->t_line == NULL))
			/*
			 * Although this would save time and RAM, some
			 * phish signatures have been built which need the
			 * blank lines
			 */
			if(messageGetMimeType(m) != TEXT)
				/* don't save two blank lines in sucession */
				return 1;

		m->body_last->t_next = (text *)cli_malloc(sizeof(text));
		if(m->body_last->t_next == NULL) {
			messageDedup(m);
			m->body_last->t_next = (text *)cli_malloc(sizeof(text));
			if(m->body_last->t_next == NULL) {
				cli_errmsg("messageAddStr: out of memory\n");
				return -1;
			}
		}

		if(data && m->body_last->t_line && (strcmp(data, lineGetData(m->body_last->t_line)) == 0))
			repeat = m->body_last->t_line;
		m->body_last = m->body_last->t_next;
	}

	if(m->body_last == NULL) {
		cli_errmsg("messageAddStr: out of memory\n");
		return -1;
	}

	m->body_last->t_next = NULL;

	if(data && *data) {
		if(repeat)
			m->body_last->t_line = lineLink(repeat);
		else {
			m->body_last->t_line = lineCreate(data);

			if(m->body_last->t_line == NULL) {
				messageDedup(m);
				m->body_last->t_line = lineCreate(data);

				if(m->body_last->t_line == NULL) {
					cli_errmsg("messageAddStr: out of memory\n");
					return -1;
				}
			}
			/* cli_chomp(m->body_last->t_text); */
			messageIsEncoding(m);
		}
	} else
		m->body_last->t_line = NULL;

	return 1;
}


usefulArg(const char *arg)
{
	if((strncasecmp(arg, "name", 4) != 0) &&
	   (strncasecmp(arg, "filename", 8) != 0) &&
	   (strncasecmp(arg, "boundary", 8) != 0) &&
	   (strncasecmp(arg, "protocol", 8) != 0) &&
	   (strncasecmp(arg, "id", 2) != 0) &&
	   (strncasecmp(arg, "number", 6) != 0) &&
	   (strncasecmp(arg, "total", 5) != 0) &&
	   (strncasecmp(arg, "type", 4) != 0)) {
		cli_dbgmsg("Discarding unwanted argument '%s'\n", arg);
		return 0;
	}
	return 1;
}


base64Flush(message *m, unsigned char *buf)
{
	cli_dbgmsg("%d trailing bytes to export\n", m->base64chars);

	if(m->base64chars) {
		unsigned char *ret = decode(m, NULL, buf, base64, FALSE);

		m->base64chars = 0;

		return ret;
	}
	return NULL;
}


messageToText(message *m)
{
	int i;
	text *first = NULL, *last = NULL;
	const text *t_line;

	assert(m != NULL);

	if(m->numberOfEncTypes == 0) {
		/*
		 * Fast copy
		 */
		for(t_line = messageGetBody(m); t_line; t_line = t_line->t_next) {
			if(first == NULL)
				first = last = cli_malloc(sizeof(text));
			else {
				last->t_next = cli_malloc(sizeof(text));
				last = last->t_next;
			}

			if(last == NULL) {
				if(first)
					textDestroy(first);
				return NULL;
			}
			if(t_line->t_line)
				last->t_line = lineLink(t_line->t_line);
			else
				last->t_line = NULL;	/* empty line */
		}
		if(last)
			last->t_next = NULL;

		return first;
	}
	/*
	 * Scan over the data a number of times once for each claimed encoding
	 * type
	 */
	for(i = 0; i < m->numberOfEncTypes; i++) {
		const encoding_type enctype = m->encodingTypes[i];

		cli_dbgmsg("messageToText: export transfer method %d = %d\n",
			i, (int)enctype);

		switch(enctype) {
			case NOENCODING:
			case BINARY:
			case EIGHTBIT:
				/*
				 * Fast copy
				 */
				for(t_line = messageGetBody(m); t_line; t_line = t_line->t_next) {
					if(first == NULL)
						first = last = cli_malloc(sizeof(text));
					else if (last) {
						last->t_next = cli_malloc(sizeof(text));
						last = last->t_next;
					}

					if(last == NULL) {
						if(first) {
							textDestroy(first);
						}
						return NULL;
					}
					if(t_line->t_line)
						last->t_line = lineLink(t_line->t_line);
					else
						last->t_line = NULL;	/* empty line */
				}
				continue;
			case UUENCODE:
				cli_warnmsg("messageToText: Unexpected attempt to handle uuencoded file\n");
				if(first) {
					if(last)
						last->t_next = NULL;
					textDestroy(first);
				}
				return NULL;
			case YENCODE:
				t_line = yEncBegin(m);

				if(t_line == NULL) {
					/*cli_warnmsg("YENCODED attachment is missing begin statement\n");*/
					if(first) {
						if(last)
							last->t_next = NULL;
						textDestroy(first);
					}
					return NULL;
				}
				t_line = t_line->t_next;
			default:
				if((i == 0) && binhexBegin(m))
					cli_warnmsg("Binhex messages not supported yet.\n");
				t_line = messageGetBody(m);
		}

		for(; t_line; t_line = t_line->t_next) {
			unsigned char data[1024];
			unsigned char *uptr;
			const char *line = lineGetData(t_line->t_line);

			if(enctype == BASE64)
				/*
				 * ignore blanks - breaks RFC which is
				 * probably the point!
				 */
				if(line == NULL)
					continue;

			assert((line == NULL) || (strlen(line) <= sizeof(data)));

			uptr = decodeLine(m, enctype, line, data, sizeof(data));

			if(uptr == NULL)
				break;

			assert(uptr <= &data[sizeof(data)]);

			if(first == NULL)
				first = last = cli_malloc(sizeof(text));
			else if (last) {
				last->t_next = cli_malloc(sizeof(text));
				last = last->t_next;
			}

			if(last == NULL)
				break;

			/*
			 * If the decoded line is the same as the encoded
			 * there's no need to take a copy, just link it.
			 * Note that the comparison is done without the
			 * trailing newline that the decoding routine may have
			 * added - that's why there's a strncmp rather than a
			 * strcmp - that'd be bad for MIME decoders, but is OK
			 * for AV software
			 */
			if((data[0] == '\n') || (data[0] == '\0'))
				last->t_line = NULL;
			else if(line && (strncmp((const char *)data, line, strlen(line)) == 0)) {
#ifdef	CL_DEBUG
				cli_dbgmsg("messageToText: decoded line is the same(%s)\n", data);
#endif
				last->t_line = lineLink(t_line->t_line);
			} else
				last->t_line = lineCreate((char *)data);

			if(line && enctype == BASE64)
				if(strchr(line, '='))
					break;
		}
		if(m->base64chars) {
			unsigned char data[4];

			memset(data, '\0', sizeof(data));
			if(decode(m, NULL, data, base64, FALSE) && data[0]) {
				if(first == NULL)
					first = last = cli_malloc(sizeof(text));
				else if (last) {
					last->t_next = cli_malloc(sizeof(text));
					last = last->t_next;
				}

				if(last != NULL)
					last->t_line = lineCreate((char *)data);
			}
			m->base64chars = 0;
		}
	}

	if(last)
		last->t_next = NULL;

	return first;
}


static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens,
			  unsigned int first, unsigned int last)
{
  /* bit buffer and huffman symbol decode variables */
  register unsigned int bit_buffer;
  register int bits_left, i;
  register unsigned short sym;
  unsigned char *i_ptr, *i_end;

  unsigned int x, y;
  int z;

  RESTORE_BITS;

  /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */
  for (x = 0; x < 20; x++) {
    READ_BITS(y, 4);
    lzx->PRETREE_len[x] = y;
  }
  BUILD_TABLE(PRETREE);

  for (x = first; x < last; ) {
    READ_HUFFSYM(PRETREE, z);
    if (z == 17) {
      /* code = 17, run of ([read 4 bits]+4) zeros */
      READ_BITS(y, 4); y += 4;
      while (y--) lens[x++] = 0;
    }
    else if (z == 18) {
      /* code = 18, run of ([read 5 bits]+20) zeros */
      READ_BITS(y, 5); y += 20;
      while (y--) lens[x++] = 0;
    }
    else if (z == 19) {
      /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */
      READ_BITS(y, 1); y += 4;
      READ_HUFFSYM(PRETREE, z);
      z = lens[x] - z; if (z < 0) z += 17;
      while (y--) lens[x++] = z;
    }
    else {
      /* code = 0 to 16, delta current length entry */
      z = lens[x] - z; if (z < 0) z += 17;
      lens[x++] = z;
    }
  }

  STORE_BITS;

  return MSPACK_ERR_OK;
}


_zip_cdir_free(zip_cdir_t *cd)
{
    zip_uint64_t i;

    if (!cd)
	return;

    for (i=0; i<cd->nentry; i++)
	_zip_entry_finalize(cd->entry+i);
    free(cd->entry);
    _zip_string_free(cd->comment);
    free(cd);
}


bool controller::is_valid_podcast_type(const std::string& /* mimetype */) {
	return true;
}


std::shared_ptr<rss_feed> controller::get_feed_by_url(const std::string& feedurl) {
	for (auto feed : feeds) {
		if (feedurl == feed->rssurl())
			return feed;
	}
	LOG(level::ERROR, "controller:get_feed_by_url failed for %s", feedurl);
	return std::shared_ptr<rss_feed>();
}


std::string controller::get_hostname_from_url(const std::string& url) {
	xmlURIPtr uri = xmlParseURI(url.c_str());
	std::string hostname;
	if (uri) {
		hostname = uri->server;
		xmlFreeURI(uri);
	}
	return hostname;
}


void controller::mark_article_read(const std::string& guid, bool read) {
	if (api) {
		api->mark_article_read(guid, read);
	}
}


void controller::export_read_information(const std::string& readinfofile) {
	std::vector<std::string> guids = rsscache->get_read_item_guids();

	std::fstream f;
	f.open(readinfofile.c_str(), std::fstream::out);
	if (f.is_open()) {
		for (auto guid : guids) {
			f << guid << std::endl;
		}
	}
}


std::string controller::prepare_message(unsigned int pos, unsigned int max) {
	if (max > 0) {
		return strprintf::fmt("(%u/%u) ", pos, max);
	}
	return "";
}


std::vector<std::shared_ptr<rss_feed>> controller::get_all_feeds_unlocked() {
	return feeds;
}


void controller::sort_feeds() {
	std::lock_guard<std::mutex> feedslock(feeds_mutex);
	std::vector<std::string> sortmethod_info = utils::tokenize(cfg.get_configvalue("feed-sort-order"), "-");
	std::string sortmethod = sortmethod_info[0];
	std::string direction = "desc";
	if (sortmethod_info.size() > 1)
		direction = sortmethod_info[1];
	if (sortmethod == "none") {
		std::stable_sort(feeds.begin(), feeds.end(), [](std::shared_ptr<rss_feed> a, std::shared_ptr<rss_feed> b) {
			return a->get_order() < b->get_order();
		});
	} else if (sortmethod == "firsttag") {
		std::stable_sort(feeds.begin(), feeds.end(), [](std::shared_ptr<rss_feed> a, std::shared_ptr<rss_feed> b) {
			if (a->get_firsttag().length() == 0 || b->get_firsttag().length() == 0) {
				return a->get_firsttag().length() > b->get_firsttag().length();
			}
			return strcasecmp(a->get_firsttag().c_str(), b->get_firsttag().c_str()) < 0;
		});
	} else if (sortmethod == "title") {
		std::stable_sort(feeds.begin(), feeds.end(), [](std::shared_ptr<rss_feed> a, std::shared_ptr<rss_feed> b) {
			return strcasecmp(a->title().c_str(), b->title().c_str()) < 0;
		});
	} else if (sortmethod == "articlecount") {
		std::stable_sort(feeds.begin(), feeds.end(), [](std::shared_ptr<rss_feed> a, std::shared_ptr<rss_feed> b) {
			return a->total_item_count() < b->total_item_count();
		});
	} else if (sortmethod == "unreadarticlecount") {
		std::stable_sort(feeds.begin(), feeds.end(), [](std::shared_ptr<rss_feed> a, std::shared_ptr<rss_feed> b) {
			return a->unread_item_count() < b->unread_item_count();
		});
	}
	if (direction == "asc") {
		std::reverse(feeds.begin(), feeds.end());
	}
}


void controller::reload_range(unsigned int start, unsigned int end, unsigned int size, bool unattended) {

	std::vector<unsigned int> v;
	for (unsigned int i=start; i<=end; ++i)
		v.push_back(i);

	auto extract = [](std::string& s, const std::string& url) {
		size_t p = url.find("//");
		p = (p == std::string::npos) ? 0 : p+2;
		std::string suff(url.substr(p));
		p = suff.find('/');
		s = suff.substr(0, p);
	};

	std::sort(v.begin(), v.end(), [&](unsigned int a, unsigned int b) {
		std::string domain1, domain2;
		extract(domain1, feeds[a]->rssurl());
		extract(domain2, feeds[b]->rssurl());
		std::reverse(domain1.begin(), domain1.end());
		std::reverse(domain2.begin(), domain2.end());
		return domain1 < domain2;
	});

	curl_handle easyhandle;

	for (auto i : v) {
		LOG(level::DEBUG, "controller::reload_range: reloading feed #%u", i);
		this->reload(i, size, unattended, &easyhandle);
	}
}


void controller::reload_urls_file() {
	urlcfg->reload();
	std::vector<std::shared_ptr<rss_feed>> new_feeds;
	unsigned int i = 0;

	for (auto url : urlcfg->get_urls()) {
		bool found = false;
		for (auto feed : feeds) {
			if (url == feed->rssurl()) {
				found = true;
				feed->set_tags(urlcfg->get_tags(url));
				feed->set_order(i);
				new_feeds.push_back(feed);
				break;
			}
		}
		if (!found) {
			try {
				bool ignore_disp = (cfg.get_configvalue("ignore-mode") == "display");
				std::shared_ptr<rss_feed> new_feed = rsscache->internalize_rssfeed(url, ignore_disp ? &ign : nullptr);
				new_feed->set_tags(urlcfg->get_tags(url));
				new_feed->set_order(i);
				new_feeds.push_back(new_feed);
			} catch(const dbexception& e) {
				LOG(level::ERROR, "controller::reload_urls_file: caught exception: %s", e.what());
				throw;
			}
		}
		i++;
	}

	v->set_tags(urlcfg->get_alltags());

	{
		std::lock_guard<std::mutex> feedslock(feeds_mutex);
		feeds = new_feeds;
	}

	sort_feeds();

	update_feedlist();
}


void controller::reload(unsigned int pos, unsigned int max, bool unattended, curl_handle *easyhandle) {
	LOG(level::DEBUG, "controller::reload: pos = %u max = %u", pos, max);
	if (pos < feeds.size()) {
		std::shared_ptr<rss_feed> oldfeed = feeds[pos];
		std::string errmsg;
		if (!unattended)
			v->set_status(strprintf::fmt(_("%sLoading %s..."), prepare_message(pos+1, max), utils::censor_url(oldfeed->rssurl())));

		bool ignore_dl = (cfg.get_configvalue("ignore-mode") == "download");

		rss_parser parser(oldfeed->rssurl(), rsscache, &cfg, ignore_dl ? &ign : nullptr, api);
		parser.set_easyhandle(easyhandle);
		LOG(level::DEBUG, "controller::reload: created parser");
		try {
			oldfeed->set_status(dl_status::DURING_DOWNLOAD);
			std::shared_ptr<rss_feed> newfeed = parser.parse();
			if (newfeed->total_item_count() > 0) {
				std::lock_guard<std::mutex> feedslock(feeds_mutex);
				save_feed(newfeed, pos);

				newfeed->clear_items();

				bool ignore_disp = (cfg.get_configvalue("ignore-mode") == "display");
				std::shared_ptr<rss_feed> feed = rsscache->internalize_rssfeed(oldfeed->rssurl(), ignore_disp ? &ign : nullptr);
				feed->set_tags(urlcfg->get_tags(oldfeed->rssurl()));
				feed->set_order(oldfeed->get_order());
				feeds[pos] = feed;
				enqueue_items(feed);

				oldfeed->clear_items();

				v->notify_itemlist_change(feeds[pos]);
				if (!unattended) {
					v->set_feedlist(feeds);
				}
			} else {
				LOG(level::DEBUG, "controller::reload: feed is empty");
			}
			oldfeed->set_status(dl_status::SUCCESS);
			v->set_status("");
		} catch (const dbexception& e) {
			errmsg = strprintf::fmt(_("Error while retrieving %s: %s"), utils::censor_url(oldfeed->rssurl()), e.what());
		} catch (const std::string& emsg) {
			errmsg = strprintf::fmt(_("Error while retrieving %s: %s"), utils::censor_url(oldfeed->rssurl()), emsg);
		} catch (rsspp::exception& e) {
			errmsg = strprintf::fmt(_("Error while retrieving %s: %s"), utils::censor_url(oldfeed->rssurl()), e.what());
		}
		if (errmsg != "") {
			oldfeed->set_status(dl_status::DL_ERROR);
			v->set_status(errmsg);
			LOG(level::USERERROR, "%s", errmsg);
		}
	} else {
		v->show_error(_("Error: invalid feed!"));
	}
}


void controller::save_feed(std::shared_ptr<rss_feed> feed, unsigned int pos) {
	if (!feed->is_empty()) {
		LOG(level::DEBUG, "controller::save_feed: feed is nonempty, saving");
		rsscache->externalize_rssfeed(feed, ign.matches_resetunread(feed->rssurl()));
		LOG(level::DEBUG, "controller::save_feed: after externalize_rssfeed");

		bool ignore_disp = (cfg.get_configvalue("ignore-mode") == "display");
		feed = rsscache->internalize_rssfeed(feed->rssurl(), ignore_disp ? &ign : nullptr);
		LOG(level::DEBUG, "controller::save_feed: after internalize_rssfeed");
		feed->set_tags(urlcfg->get_tags(feed->rssurl()));
		{
			unsigned int order = feeds[pos]->get_order();
			std::lock_guard<std::mutex> itemlock(feeds[pos]->item_mutex);
			feeds[pos]->clear_items();
			feed->set_order(order);
		}
		feeds[pos] = feed;
		v->notify_itemlist_change(feeds[pos]);
	} else {
		LOG(level::DEBUG, "controller::save_feed: feed is empty, not saving");
	}
}


PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
{
	var_entries *orig_var_entries = (*var_hash)->last;
	zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
	int result;

	result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU);

	if (!result) {
		/* If the unserialization failed, mark all elements that have been added to var_hash
		 * as NULL. This will forbid their use by other unserialize() calls in the same
		 * unserialization context. */
		var_entries *e = orig_var_entries;
		zend_long s = orig_used_slots;
		while (e) {
			for (; s < e->used_slots; s++) {
				e->data[s] = NULL;
			}

			e = e->next;
			s = 0;
		}
	}

	return result;
}


u32 __skb_get_poff(const struct sk_buff *skb, void *data,
		   const struct flow_keys *keys, int hlen)
{
	u32 poff = keys->control.thoff;

	/* skip L4 headers for fragments after the first */
	if ((keys->control.flags & FLOW_DIS_IS_FRAGMENT) &&
	    !(keys->control.flags & FLOW_DIS_FIRST_FRAG))
		return poff;

	switch (keys->basic.ip_proto) {
	case IPPROTO_TCP: {
		/* access doff as u8 to avoid unaligned access */
		const u8 *doff;
		u8 _doff;

		doff = __skb_header_pointer(skb, poff + 12, sizeof(_doff),
					    data, hlen, &_doff);
		if (!doff)
			return poff;

		poff += max_t(u32, sizeof(struct tcphdr), (*doff & 0xF0) >> 2);
		break;
	}
	case IPPROTO_UDP:
	case IPPROTO_UDPLITE:
		poff += sizeof(struct udphdr);
		break;
	/* For the rest, we do not really care about header
	 * extensions at this point for now.
	 */
	case IPPROTO_ICMP:
		poff += sizeof(struct icmphdr);
		break;
	case IPPROTO_ICMPV6:
		poff += sizeof(struct icmp6hdr);
		break;
	case IPPROTO_IGMP:
		poff += sizeof(struct igmphdr);
		break;
	case IPPROTO_DCCP:
		poff += sizeof(struct dccp_hdr);
		break;
	case IPPROTO_SCTP:
		poff += sizeof(struct sctphdr);
		break;
	}

	return poff;
}


u32 __skb_get_hash_symmetric(struct sk_buff *skb)
{
	struct flow_keys keys;

	__flow_hash_secret_init();

	memset(&keys, 0, sizeof(keys));
	__skb_flow_dissect(skb, &flow_keys_dissector_symmetric, &keys,
			   NULL, 0, 0, 0,
			   FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);

	return __flow_hash_from_keys(&keys, hashrnd);
}


static int ip_mkroute_input(struct sk_buff *skb,
			    struct fib_result *res,
			    struct in_device *in_dev,
			    __be32 daddr, __be32 saddr, u32 tos)
{
#ifdef CONFIG_IP_ROUTE_MULTIPATH
	if (res->fi && res->fi->fib_nhs > 1) {
		int h = fib_multipath_hash(res->fi, NULL, skb);

		fib_select_multipath(res, h);
	}
#endif

	/* create a routing cache entry */
	return __mkroute_input(skb, res, in_dev, daddr, saddr, tos);
}


static void set_lwt_redirect(struct rtable *rth)
{
	if (lwtunnel_output_redirect(rth->dst.lwtstate)) {
		rth->dst.lwtstate->orig_output = rth->dst.output;
		rth->dst.output = lwtunnel_output;
	}

	if (lwtunnel_input_redirect(rth->dst.lwtstate)) {
		rth->dst.lwtstate->orig_input = rth->dst.input;
		rth->dst.input = lwtunnel_input;
	}
}


QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e)
{
    // If parsing on behalf of a QPDF object and want to give a
    // warning, we can warn through the object. If parsing for some
    // other reason, such as an explicit creation of an object from a
    // string, then just throw the exception.
    if (qpdf)
    {
        QPDF::Warner::warn(qpdf, e);
    }
    else
    {
        throw e;
    }
}


_gcry_mpi_ec_decode_point (mpi_point_t result, gcry_mpi_t value, mpi_ec_t ec)
{
  gcry_err_code_t rc;

  if (ec && ec->dialect == ECC_DIALECT_ED25519)
    rc = _gcry_ecc_eddsa_decodepoint (value, ec, result, NULL, NULL);
  else if (ec && ec->model == MPI_EC_MONTGOMERY)
    rc = _gcry_ecc_mont_decodepoint (value, ec, result);
  else
    rc = _gcry_ecc_os2ec (result, value);

  return rc;
}


test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags)
{
  ECC_public_key pk;
  gcry_mpi_t test;
  mpi_point_struct R_;
  gcry_mpi_t x0, x1;
  mpi_ec_t ec;

  if (DBG_CIPHER)
    log_debug ("Testing ECDH only key.\n");

  point_init (&R_);

  pk.E = _gcry_ecc_curve_copy (sk->E);
  point_init (&pk.Q);
  point_set (&pk.Q, &sk->Q);

  if ((flags & PUBKEY_FLAG_DJB_TWEAK))
    {
      char *rndbuf;

      test = mpi_new (256);
      rndbuf = _gcry_random_bytes (32, GCRY_WEAK_RANDOM);
      rndbuf[0] &= 0x7f;  /* Clear bit 255. */
      rndbuf[0] |= 0x40;  /* Set bit 254.   */
      rndbuf[31] &= 0xf8; /* Clear bits 2..0 so that d mod 8 == 0  */
      _gcry_mpi_set_buffer (test, rndbuf, 32, 0);
      xfree (rndbuf);
    }
  else
    {
      test = mpi_new (nbits);
      _gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
    }

  ec = _gcry_mpi_ec_p_internal_new (pk.E.model, pk.E.dialect, flags,
                                    pk.E.p, pk.E.a, pk.E.b);
  x0 = mpi_new (0);
  x1 = mpi_new (0);

  /* R_ = hkQ  <=>  R_ = hkdG  */
  _gcry_mpi_ec_mul_point (&R_, test, &pk.Q, ec);
  if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
    _gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec);
  if (_gcry_mpi_ec_get_affine (x0, NULL, &R_, ec))
    log_fatal ("ecdh: Failed to get affine coordinates for hkQ\n");

  _gcry_mpi_ec_mul_point (&R_, test, &pk.E.G, ec);
  _gcry_mpi_ec_mul_point (&R_, sk->d, &R_, ec);
  /* R_ = hdkG */
  if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
    _gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec);

  if (_gcry_mpi_ec_get_affine (x1, NULL, &R_, ec))
    log_fatal ("ecdh: Failed to get affine coordinates for hdkG\n");

  if (mpi_cmp (x0, x1))
    {
      log_fatal ("ECDH test failed.\n");
    }

  mpi_free (x0);
  mpi_free (x1);
  _gcry_mpi_ec_free (ec);

  point_free (&pk.Q);
  _gcry_ecc_curve_free (&pk.E);

  point_free (&R_);
  mpi_free (test);
}


sub_points_edwards (mpi_point_t result,
                    mpi_point_t p1, mpi_point_t p2,
                    mpi_ec_t ctx)
{
  mpi_point_t p2i = _gcry_mpi_point_new (0);
  point_set (p2i, p2);
  mpi_sub (p2i->x, ctx->p, p2i->x);
  add_points_edwards (result, p1, p2i, ctx);
  _gcry_mpi_point_release (p2i);
}


ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
{
  gcry_err_code_t rc;
  struct pk_encoding_ctx ctx;
  gcry_sexp_t l1 = NULL;
  char *curvename = NULL;
  gcry_mpi_t mpi_g = NULL;
  gcry_mpi_t mpi_q = NULL;
  gcry_mpi_t sig_r = NULL;
  gcry_mpi_t sig_s = NULL;
  gcry_mpi_t data = NULL;
  ECC_public_key pk;
  int sigflags;

  memset (&pk, 0, sizeof pk);
  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY,
                                   ecc_get_nbits (s_keyparms));

  /* Extract the data.  */
  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
  if (rc)
    goto leave;
  if (DBG_CIPHER)
    log_mpidump ("ecc_verify data", data);

  /*
   * Extract the signature value.
   */
  rc = _gcry_pk_util_preparse_sigval (s_sig, ecc_names, &l1, &sigflags);
  if (rc)
    goto leave;
  rc = sexp_extract_param (l1, NULL, (sigflags & PUBKEY_FLAG_EDDSA)? "/rs":"rs",
                           &sig_r, &sig_s, NULL);
  if (rc)
    goto leave;
  if (DBG_CIPHER)
    {
      log_mpidump ("ecc_verify  s_r", sig_r);
      log_mpidump ("ecc_verify  s_s", sig_s);
    }
  if ((ctx.flags & PUBKEY_FLAG_EDDSA) ^ (sigflags & PUBKEY_FLAG_EDDSA))
    {
      rc = GPG_ERR_CONFLICT; /* Inconsistent use of flag/algoname.  */
      goto leave;
    }

  /*
   * Extract the key.
   */
  if ((ctx.flags & PUBKEY_FLAG_PARAM))
    rc = sexp_extract_param (s_keyparms, NULL, "-p?a?b?g?n?h?/q",
                             &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n,
                             &pk.E.h, &mpi_q, NULL);
  else
    rc = sexp_extract_param (s_keyparms, NULL, "/q",
                             &mpi_q, NULL);
  if (rc)
    goto leave;
  if (mpi_g)
    {
      point_init (&pk.E.G);
      rc = _gcry_ecc_os2ec (&pk.E.G, mpi_g);
      if (rc)
        goto leave;
    }
  /* Add missing parameters using the optional curve parameter.  */
  sexp_release (l1);
  l1 = sexp_find_token (s_keyparms, "curve", 5);
  if (l1)
    {
      curvename = sexp_nth_string (l1, 1);
      if (curvename)
        {
          rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL);
          if (rc)
            goto leave;
        }
    }
  /* Guess required fields if a curve parameter has not been given.
     FIXME: This is a crude hacks.  We need to fix that.  */
  if (!curvename)
    {
      pk.E.model = ((sigflags & PUBKEY_FLAG_EDDSA)
                    ? MPI_EC_EDWARDS
                    : MPI_EC_WEIERSTRASS);
      pk.E.dialect = ((sigflags & PUBKEY_FLAG_EDDSA)
                      ? ECC_DIALECT_ED25519
                      : ECC_DIALECT_STANDARD);
      if (!pk.E.h)
	pk.E.h = mpi_const (MPI_C_ONE);
    }

  if (DBG_CIPHER)
    {
      log_debug ("ecc_verify info: %s/%s%s\n",
                 _gcry_ecc_model2str (pk.E.model),
                 _gcry_ecc_dialect2str (pk.E.dialect),
                 (sigflags & PUBKEY_FLAG_EDDSA)? "+EdDSA":"");
      if (pk.E.name)
        log_debug  ("ecc_verify name: %s\n", pk.E.name);
      log_printmpi ("ecc_verify    p", pk.E.p);
      log_printmpi ("ecc_verify    a", pk.E.a);
      log_printmpi ("ecc_verify    b", pk.E.b);
      log_printpnt ("ecc_verify  g",   &pk.E.G, NULL);
      log_printmpi ("ecc_verify    n", pk.E.n);
      log_printmpi ("ecc_verify    h", pk.E.h);
      log_printmpi ("ecc_verify    q", mpi_q);
    }
  if (!pk.E.p || !pk.E.a || !pk.E.b || !pk.E.G.x || !pk.E.n || !pk.E.h || !mpi_q)
    {
      rc = GPG_ERR_NO_OBJ;
      goto leave;
    }

  /*
   * Verify the signature.
   */
  if ((sigflags & PUBKEY_FLAG_EDDSA))
    {
      rc = _gcry_ecc_eddsa_verify (data, &pk, sig_r, sig_s,
                                   ctx.hash_algo, mpi_q);
    }
  else if ((sigflags & PUBKEY_FLAG_GOST))
    {
      point_init (&pk.Q);
      rc = _gcry_ecc_os2ec (&pk.Q, mpi_q);
      if (rc)
        goto leave;

      rc = _gcry_ecc_gost_verify (data, &pk, sig_r, sig_s);
    }
  else
    {
      point_init (&pk.Q);
      if (pk.E.dialect == ECC_DIALECT_ED25519)
        {
          mpi_ec_t ec;

          /* Fixme: Factor the curve context setup out of eddsa_verify
             and ecdsa_verify. So that we don't do it twice.  */
          ec = _gcry_mpi_ec_p_internal_new (pk.E.model, pk.E.dialect, 0,
                                            pk.E.p, pk.E.a, pk.E.b);

          rc = _gcry_ecc_eddsa_decodepoint (mpi_q, ec, &pk.Q, NULL, NULL);
          _gcry_mpi_ec_free (ec);
        }
      else
        {
          rc = _gcry_ecc_os2ec (&pk.Q, mpi_q);
        }
      if (rc)
        goto leave;

      if (mpi_is_opaque (data))
        {
          const void *abuf;
          unsigned int abits, qbits;
          gcry_mpi_t a;

          qbits = mpi_get_nbits (pk.E.n);

          abuf = mpi_get_opaque (data, &abits);
          rc = _gcry_mpi_scan (&a, GCRYMPI_FMT_USG, abuf, (abits+7)/8, NULL);
          if (!rc)
            {
              if (abits > qbits)
                mpi_rshift (a, a, abits - qbits);

              rc = _gcry_ecc_ecdsa_verify (a, &pk, sig_r, sig_s);
              _gcry_mpi_release (a);
            }
        }
      else
        rc = _gcry_ecc_ecdsa_verify (data, &pk, sig_r, sig_s);
    }

 leave:
  _gcry_mpi_release (pk.E.p);
  _gcry_mpi_release (pk.E.a);
  _gcry_mpi_release (pk.E.b);
  _gcry_mpi_release (mpi_g);
  point_free (&pk.E.G);
  _gcry_mpi_release (pk.E.n);
  _gcry_mpi_release (pk.E.h);
  _gcry_mpi_release (mpi_q);
  point_free (&pk.Q);
  _gcry_mpi_release (data);
  _gcry_mpi_release (sig_r);
  _gcry_mpi_release (sig_s);
  xfree (curvename);
  sexp_release (l1);
  _gcry_pk_util_free_encoding_ctx (&ctx);
  if (DBG_CIPHER)
    log_debug ("ecc_verify    => %s\n", rc?gpg_strerror (rc):"Good");
  return rc;
}


nist_generate_key (ECC_secret_key *sk, elliptic_curve_t *E, mpi_ec_t ctx,
                   int flags, unsigned int nbits,
                   gcry_mpi_t *r_x, gcry_mpi_t *r_y)
{
  mpi_point_struct Q;
  gcry_random_level_t random_level;
  gcry_mpi_t x, y;
  const unsigned int pbits = mpi_get_nbits (E->p);

  point_init (&Q);

  if ((flags & PUBKEY_FLAG_TRANSIENT_KEY))
    random_level = GCRY_STRONG_RANDOM;
  else
    random_level = GCRY_VERY_STRONG_RANDOM;

  /* Generate a secret.  */
  if (ctx->dialect == ECC_DIALECT_ED25519 || (flags & PUBKEY_FLAG_DJB_TWEAK))
    {
      char *rndbuf;

      sk->d = mpi_snew (256);
      rndbuf = _gcry_random_bytes_secure (32, random_level);
      rndbuf[0] &= 0x7f;  /* Clear bit 255. */
      rndbuf[0] |= 0x40;  /* Set bit 254.   */
      rndbuf[31] &= 0xf8; /* Clear bits 2..0 so that d mod 8 == 0  */
      _gcry_mpi_set_buffer (sk->d, rndbuf, 32, 0);
      xfree (rndbuf);
    }
  else
    sk->d = _gcry_dsa_gen_k (E->n, random_level);

  /* Compute Q.  */
  _gcry_mpi_ec_mul_point (&Q, sk->d, &E->G, ctx);

  /* Copy the stuff to the key structures. */
  sk->E.model = E->model;
  sk->E.dialect = E->dialect;
  sk->E.p = mpi_copy (E->p);
  sk->E.a = mpi_copy (E->a);
  sk->E.b = mpi_copy (E->b);
  point_init (&sk->E.G);
  point_set (&sk->E.G, &E->G);
  sk->E.n = mpi_copy (E->n);
  sk->E.h = mpi_copy (E->h);
  point_init (&sk->Q);

  x = mpi_new (pbits);
  if (r_y == NULL)
    y = NULL;
  else
    y = mpi_new (pbits);
  if (_gcry_mpi_ec_get_affine (x, y, &Q, ctx))
    log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");

  /* We want the Q=(x,y) be a "compliant key" in terms of the
   * http://tools.ietf.org/html/draft-jivsov-ecc-compact, which simply
   * means that we choose either Q=(x,y) or -Q=(x,p-y) such that we
   * end up with the min(y,p-y) as the y coordinate.  Such a public
   * key allows the most efficient compression: y can simply be
   * dropped because we know that it's a minimum of the two
   * possibilities without any loss of security.  Note that we don't
   * do that for Ed25519 so that we do not violate the special
   * construction of the secret key.  */
  if (r_y == NULL || E->dialect == ECC_DIALECT_ED25519)
    point_set (&sk->Q, &Q);
  else
    {
      gcry_mpi_t negative;

      negative = mpi_new (pbits);

      if (E->model == MPI_EC_WEIERSTRASS)
        mpi_sub (negative, E->p, y);      /* negative = p - y */
      else
        mpi_sub (negative, E->p, x);      /* negative = p - x */

      if (mpi_cmp (negative, y) < 0)   /* p - y < p */
        {
          /* We need to end up with -Q; this assures that new Q's y is
             the smallest one */
          if (E->model == MPI_EC_WEIERSTRASS)
            {
              mpi_free (y);
              y = negative;
            }
          else
            {
              mpi_free (x);
              x = negative;
            }
          mpi_sub (sk->d, E->n, sk->d);   /* d = order - d */
          mpi_point_set (&sk->Q, x, y, mpi_const (MPI_C_ONE));

          if (DBG_CIPHER)
            log_debug ("ecgen converted Q to a compliant point\n");
        }
      else /* p - y >= p */
        {
          /* No change is needed exactly 50% of the time: just copy. */
          mpi_free (negative);
          point_set (&sk->Q, &Q);
          if (DBG_CIPHER)
            log_debug ("ecgen didn't need to convert Q to a compliant point\n");
        }
    }

  *r_x = x;
  if (r_y)
    *r_y = y;

  point_free (&Q);
  /* Now we can test our keys (this should never fail!).  */
  if ((flags & PUBKEY_FLAG_NO_KEYTEST))
    ; /* User requested to skip the test.  */
  else if (sk->E.model != MPI_EC_MONTGOMERY)
    test_keys (sk, nbits - 64);
  else
    test_ecdh_only_keys (sk, nbits - 64, flags);

  return 0;
}


copy_gray_row (gint     *dest,
               guchar   *src,
               gint      width,
               gboolean  has_alpha)
{
        gint i;
        guchar *p;

        p = src;
        for (i = 0; i < width; i++) {
                int pr, pg, pb, pv;

                pr = *p++;
                pg = *p++;
                pb = *p++;

                if (has_alpha) {
                        int pa = *p++;

                        /* Premul alpha to simulate it */
                        if (pa > 0) {
                                pr = pr * pa / 255;
                                pg = pg * pa / 255;
                                pb = pb * pa / 255;
                        } else {
                                pr = pg = pb = 0;
                        }
                }

                /* Calculate value MAX(MAX(r,g),b) */
                pv = pr > pg ? pr : pg;
                pv = pv > pb ? pv : pb;

                *dest++ = pv;
        }
}


tiff_save_write (thandle_t handle, tdata_t buf, tsize_t size)
{
        TiffSaveContext *context = (TiffSaveContext *)handle;

        /* Modify buffer length */
        if (context->pos + size > context->used)
                context->used = context->pos + size;

        /* Realloc */
        if (context->used > context->allocated) {
                context->buffer = g_realloc (context->buffer, context->pos + size);
                context->allocated = context->used;
        }

        /* Now copy the data */
        memcpy (context->buffer + context->pos, buf, size);

        /* Update pos */
        context->pos += size;

        return size;
}


static MagickBooleanType load_hierarchy(Image *image,XCFDocInfo *inDocInfo,
   XCFLayerInfo *inLayer, ExceptionInfo *exception)
{
  MagickOffsetType
    saved_pos,
    offset,
    junk;

  size_t
    width,
    height,
    bytes_per_pixel;

  width=ReadBlobMSBLong(image);
  (void) width;
  height=ReadBlobMSBLong(image);
  (void) height;
  bytes_per_pixel=inDocInfo->bytes_per_pixel=ReadBlobMSBLong(image);
  (void) bytes_per_pixel;

  /* load in the levels...we make sure that the number of levels
   *  calculated when the TileManager was created is the same
   *  as the number of levels found in the file.
   */
  offset=(MagickOffsetType) ReadBlobMSBLong(image);  /* top level */

  /* discard offsets for layers below first, if any.
   */
  do
  {
    junk=(MagickOffsetType) ReadBlobMSBLong(image);
  }
  while (junk != 0);

  /* save the current position as it is where the
   *  next level offset is stored.
   */
  saved_pos=TellBlob(image);

  /* seek to the level offset */
  if (SeekBlob(image, offset, SEEK_SET) != offset)
    ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile",
      image->filename);

  /* read in the level */
  if (load_level (image, inDocInfo, inLayer, exception) == 0)
    return(MagickFalse);
  /* restore the saved position so we'll be ready to
   *  read the next offset.
   */
  offset=SeekBlob(image, saved_pos, SEEK_SET);
  return(MagickTrue);
}


static inline void ModulateHWB(const double percent_hue,
  const double percent_whiteness,const double percent_blackness,double *red,
  double *green,double *blue)
{
  double
    blackness,
    hue,
    whiteness;

  /*
    Increase or decrease color blackness, whiteness, or hue.
  */
  ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
  hue+=fmod((percent_hue-100.0),200.0)/200.0;
  blackness*=0.01*percent_blackness;
  whiteness*=0.01*percent_whiteness;
  ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
}


int imagetobmp(opj_image_t * image, const char *outfile)
{
    int w, h;
    int i, pad;
    FILE *fdest = NULL;
    int adjustR, adjustG, adjustB;

    if (image->comps[0].prec < 8) {
        fprintf(stderr, "imagetobmp: Unsupported precision: %d\n",
                image->comps[0].prec);
        return 1;
    }
    if (image->numcomps >= 3 && image->comps[0].dx == image->comps[1].dx
            && image->comps[1].dx == image->comps[2].dx
            && image->comps[0].dy == image->comps[1].dy
            && image->comps[1].dy == image->comps[2].dy
            && image->comps[0].prec == image->comps[1].prec
            && image->comps[1].prec == image->comps[2].prec
            && image->comps[0].sgnd == image->comps[1].sgnd
            && image->comps[1].sgnd == image->comps[2].sgnd) {

        /* -->> -->> -->> -->>
        24 bits color
        <<-- <<-- <<-- <<-- */

        fdest = fopen(outfile, "wb");
        if (!fdest) {
            fprintf(stderr, "ERROR -> failed to open %s for writing\n", outfile);
            return 1;
        }

        w = (int)image->comps[0].w;
        h = (int)image->comps[0].h;

        fprintf(fdest, "BM");

        /* FILE HEADER */
        /* ------------- */
        fprintf(fdest, "%c%c%c%c",
                (OPJ_UINT8)(h * w * 3 + 3 * h * (w % 2) + 54) & 0xff,
                (OPJ_UINT8)((h * w * 3 + 3 * h * (w % 2) + 54) >> 8) & 0xff,
                (OPJ_UINT8)((h * w * 3 + 3 * h * (w % 2) + 54) >> 16) & 0xff,
                (OPJ_UINT8)((h * w * 3 + 3 * h * (w % 2) + 54) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff, ((0) >> 16) & 0xff,
                ((0) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (54) & 0xff, ((54) >> 8) & 0xff, ((54) >> 16) & 0xff,
                ((54) >> 24) & 0xff);

        /* INFO HEADER   */
        /* ------------- */
        fprintf(fdest, "%c%c%c%c", (40) & 0xff, ((40) >> 8) & 0xff, ((40) >> 16) & 0xff,
                ((40) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (OPJ_UINT8)((w) & 0xff),
                (OPJ_UINT8)((w) >> 8) & 0xff,
                (OPJ_UINT8)((w) >> 16) & 0xff,
                (OPJ_UINT8)((w) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (OPJ_UINT8)((h) & 0xff),
                (OPJ_UINT8)((h) >> 8) & 0xff,
                (OPJ_UINT8)((h) >> 16) & 0xff,
                (OPJ_UINT8)((h) >> 24) & 0xff);
        fprintf(fdest, "%c%c", (1) & 0xff, ((1) >> 8) & 0xff);
        fprintf(fdest, "%c%c", (24) & 0xff, ((24) >> 8) & 0xff);
        fprintf(fdest, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff, ((0) >> 16) & 0xff,
                ((0) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (OPJ_UINT8)(3 * h * w + 3 * h * (w % 2)) & 0xff,
                (OPJ_UINT8)((h * w * 3 + 3 * h * (w % 2)) >> 8) & 0xff,
                (OPJ_UINT8)((h * w * 3 + 3 * h * (w % 2)) >> 16) & 0xff,
                (OPJ_UINT8)((h * w * 3 + 3 * h * (w % 2)) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (7834) & 0xff, ((7834) >> 8) & 0xff,
                ((7834) >> 16) & 0xff, ((7834) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (7834) & 0xff, ((7834) >> 8) & 0xff,
                ((7834) >> 16) & 0xff, ((7834) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff, ((0) >> 16) & 0xff,
                ((0) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff, ((0) >> 16) & 0xff,
                ((0) >> 24) & 0xff);

        if (image->comps[0].prec > 8) {
            adjustR = (int)image->comps[0].prec - 8;
            printf("BMP CONVERSION: Truncating component 0 from %d bits to 8 bits\n",
                   image->comps[0].prec);
        } else {
            adjustR = 0;
        }
        if (image->comps[1].prec > 8) {
            adjustG = (int)image->comps[1].prec - 8;
            printf("BMP CONVERSION: Truncating component 1 from %d bits to 8 bits\n",
                   image->comps[1].prec);
        } else {
            adjustG = 0;
        }
        if (image->comps[2].prec > 8) {
            adjustB = (int)image->comps[2].prec - 8;
            printf("BMP CONVERSION: Truncating component 2 from %d bits to 8 bits\n",
                   image->comps[2].prec);
        } else {
            adjustB = 0;
        }

        for (i = 0; i < w * h; i++) {
            OPJ_UINT8 rc, gc, bc;
            int r, g, b;

            r = image->comps[0].data[w * h - ((i) / (w) + 1) * w + (i) % (w)];
            r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
            if (adjustR > 0) {
                r = ((r >> adjustR) + ((r >> (adjustR - 1)) % 2));
            }
            if (r > 255) {
                r = 255;
            } else if (r < 0) {
                r = 0;
            }
            rc = (OPJ_UINT8)r;

            g = image->comps[1].data[w * h - ((i) / (w) + 1) * w + (i) % (w)];
            g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
            if (adjustG > 0) {
                g = ((g >> adjustG) + ((g >> (adjustG - 1)) % 2));
            }
            if (g > 255) {
                g = 255;
            } else if (g < 0) {
                g = 0;
            }
            gc = (OPJ_UINT8)g;

            b = image->comps[2].data[w * h - ((i) / (w) + 1) * w + (i) % (w)];
            b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
            if (adjustB > 0) {
                b = ((b >> adjustB) + ((b >> (adjustB - 1)) % 2));
            }
            if (b > 255) {
                b = 255;
            } else if (b < 0) {
                b = 0;
            }
            bc = (OPJ_UINT8)b;

            fprintf(fdest, "%c%c%c", bc, gc, rc);

            if ((i + 1) % w == 0) {
                for (pad = ((3 * w) % 4) ? (4 - (3 * w) % 4) : 0; pad > 0; pad--) { /* ADD */
                    fprintf(fdest, "%c", 0);
                }
            }
        }
        fclose(fdest);
    } else {            /* Gray-scale */

        /* -->> -->> -->> -->>
        8 bits non code (Gray scale)
        <<-- <<-- <<-- <<-- */

        fdest = fopen(outfile, "wb");
        if (!fdest) {
            fprintf(stderr, "ERROR -> failed to open %s for writing\n", outfile);
            return 1;
        }
        if (image->numcomps > 1) {
            fprintf(stderr, "imagetobmp: only first component of %d is used.\n",
                    image->numcomps);
        }
        w = (int)image->comps[0].w;
        h = (int)image->comps[0].h;

        fprintf(fdest, "BM");

        /* FILE HEADER */
        /* ------------- */
        fprintf(fdest, "%c%c%c%c", (OPJ_UINT8)(h * w + 54 + 1024 + h * (w % 2)) & 0xff,
                (OPJ_UINT8)((h * w + 54 + 1024 + h * (w % 2)) >> 8) & 0xff,
                (OPJ_UINT8)((h * w + 54 + 1024 + h * (w % 2)) >> 16) & 0xff,
                (OPJ_UINT8)((h * w + 54 + 1024 + w * (w % 2)) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff, ((0) >> 16) & 0xff,
                ((0) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (54 + 1024) & 0xff, ((54 + 1024) >> 8) & 0xff,
                ((54 + 1024) >> 16) & 0xff,
                ((54 + 1024) >> 24) & 0xff);

        /* INFO HEADER */
        /* ------------- */
        fprintf(fdest, "%c%c%c%c", (40) & 0xff, ((40) >> 8) & 0xff, ((40) >> 16) & 0xff,
                ((40) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (OPJ_UINT8)((w) & 0xff),
                (OPJ_UINT8)((w) >> 8) & 0xff,
                (OPJ_UINT8)((w) >> 16) & 0xff,
                (OPJ_UINT8)((w) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (OPJ_UINT8)((h) & 0xff),
                (OPJ_UINT8)((h) >> 8) & 0xff,
                (OPJ_UINT8)((h) >> 16) & 0xff,
                (OPJ_UINT8)((h) >> 24) & 0xff);
        fprintf(fdest, "%c%c", (1) & 0xff, ((1) >> 8) & 0xff);
        fprintf(fdest, "%c%c", (8) & 0xff, ((8) >> 8) & 0xff);
        fprintf(fdest, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff, ((0) >> 16) & 0xff,
                ((0) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (OPJ_UINT8)(h * w + h * (w % 2)) & 0xff,
                (OPJ_UINT8)((h * w + h * (w % 2)) >> 8) &  0xff,
                (OPJ_UINT8)((h * w + h * (w % 2)) >> 16) & 0xff,
                (OPJ_UINT8)((h * w + h * (w % 2)) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (7834) & 0xff, ((7834) >> 8) & 0xff,
                ((7834) >> 16) & 0xff, ((7834) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (7834) & 0xff, ((7834) >> 8) & 0xff,
                ((7834) >> 16) & 0xff, ((7834) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (256) & 0xff, ((256) >> 8) & 0xff,
                ((256) >> 16) & 0xff, ((256) >> 24) & 0xff);
        fprintf(fdest, "%c%c%c%c", (256) & 0xff, ((256) >> 8) & 0xff,
                ((256) >> 16) & 0xff, ((256) >> 24) & 0xff);

        if (image->comps[0].prec > 8) {
            adjustR = (int)image->comps[0].prec - 8;
            printf("BMP CONVERSION: Truncating component 0 from %d bits to 8 bits\n",
                   image->comps[0].prec);
        } else {
            adjustR = 0;
        }

        for (i = 0; i < 256; i++) {
            fprintf(fdest, "%c%c%c%c", i, i, i, 0);
        }

        for (i = 0; i < w * h; i++) {
            int r;

            r = image->comps[0].data[w * h - ((i) / (w) + 1) * w + (i) % (w)];
            r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
            if (adjustR > 0) {
                r = ((r >> adjustR) + ((r >> (adjustR - 1)) % 2));
            }
            if (r > 255) {
                r = 255;
            } else if (r < 0) {
                r = 0;
            }

            fprintf(fdest, "%c", (OPJ_UINT8)r);

            if ((i + 1) % w == 0) {
                for (pad = (w % 4) ? (4 - w % 4) : 0; pad > 0; pad--) { /* ADD */
                    fprintf(fdest, "%c", 0);
                }
            }
        }
        fclose(fdest);
    }

    return 0;
}


static void bmp24toimage(const OPJ_UINT8* pData, OPJ_UINT32 stride,
                         opj_image_t* image)
{
    int index;
    OPJ_UINT32 width, height;
    OPJ_UINT32 x, y;
    const OPJ_UINT8 *pSrc = NULL;

    width  = image->comps[0].w;
    height = image->comps[0].h;

    index = 0;
    pSrc = pData + (height - 1U) * stride;
    for (y = 0; y < height; y++) {
        for (x = 0; x < width; x++) {
            image->comps[0].data[index] = (OPJ_INT32)pSrc[3 * x + 2]; /* R */
            image->comps[1].data[index] = (OPJ_INT32)pSrc[3 * x + 1]; /* G */
            image->comps[2].data[index] = (OPJ_INT32)pSrc[3 * x + 0]; /* B */
            index++;
        }
        pSrc -= stride;
    }
}


static int tcmu_glfs_flush(struct tcmu_device *dev,
                           struct tcmulib_cmd *cmd)
{
	struct glfs_state *state = tcmu_get_dev_private(dev);
	glfs_cbk_cookie *cookie;

	cookie = calloc(1, sizeof(*cookie));
	if (!cookie) {
		tcmu_err("Could not allocate cookie: %m\n");
		goto out;
	}
	cookie->dev = dev;
	cookie->cmd = cmd;
	cookie->length = 0;
	cookie->op = TCMU_GLFS_FLUSH;

	if (glfs_fdatasync_async(state->gfd, glfs_async_cbk, cookie) < 0) {
		tcmu_err("glfs_fdatasync_async failed: %m\n");
		goto out;
	}

	return 0;

out:
	free(cookie);
	return SAM_STAT_TASK_SET_FULL;
}


static int gluster_cache_add(gluster_server *dst, glfs_t *fs, char* cfgstring)
{
	struct gluster_cacheconn *entry;
	char* cfg_copy = NULL;

	entry = calloc(1, sizeof(gluster_cacheconn));
	if (!entry)
		goto error;

	entry->volname = strdup(dst->volname);

	entry->server = calloc(1, sizeof(gluster_hostdef));
	if (!entry->server)
		goto error;

	entry->server->type = dst->server->type;

	if (entry->server->type == GLUSTER_TRANSPORT_UNIX) {
		entry->server->u.uds.socket = strdup(dst->server->u.uds.socket);
	} else {
		entry->server->u.inet.addr = strdup(dst->server->u.inet.addr);
		entry->server->u.inet.port = strdup(dst->server->u.inet.port);
	}

	entry->fs = fs;

	cfg_copy = strdup(cfgstring);
	darray_init(entry->cfgstring);
	darray_append(entry->cfgstring, cfg_copy);

	darray_append(cache, entry);

	return 0;

 error:
	return -1;
}


static int tcmu_glfs_open(struct tcmu_device *dev)
{
	struct glfs_state *gfsp;
	int ret = 0;
	char *config;
	struct stat st;

	gfsp = calloc(1, sizeof(*gfsp));
	if (!gfsp)
		return -ENOMEM;

	tcmu_set_dev_private(dev, gfsp);

	config = tcmu_get_path(dev);
	if (!config) {
		goto fail;
	}

	gfsp->fs = tcmu_create_glfs_object(config, &gfsp->hosts);
	if (!gfsp->fs) {
		tcmu_err("tcmu_create_glfs_object failed\n");
		goto fail;
	}

	gfsp->gfd = glfs_open(gfsp->fs, gfsp->hosts->path, ALLOWED_BSOFLAGS);
	if (!gfsp->gfd) {
		tcmu_err("glfs_open failed: %m\n");
		goto unref;
	}

	ret = glfs_lstat(gfsp->fs, gfsp->hosts->path, &st);
	if (ret) {
		tcmu_err("glfs_lstat failed: %m\n");
		goto unref;
	}

	if (st.st_size != tcmu_get_device_size(dev)) {
		tcmu_err("device size and backing size disagree: "
		       "device %lld backing %lld\n",
		       tcmu_get_device_size(dev),
		       (long long) st.st_size);
		goto unref;
	}

	return 0;

unref:
	gluster_cache_refresh(gfsp->fs, tcmu_get_path(dev));

fail:
	if (gfsp->gfd)
		glfs_close(gfsp->gfd);
	gluster_free_server(&gfsp->hosts);
	free(gfsp);

	return -EIO;
}


gluster_compare_hosts(gluster_hostdef *src_server, gluster_hostdef *dst_server)
{
	if (src_server->type != dst_server->type)
		return false;

	switch (src_server->type) {
		case GLUSTER_TRANSPORT_UNIX:
			if (!strcmp(src_server->u.uds.socket, dst_server->u.uds.socket))
				return true;
			break;
		case GLUSTER_TRANSPORT_TCP:
		case GLUSTER_TRANSPORT_RDMA:
			if (!strcmp(src_server->u.inet.addr, dst_server->u.inet.addr)
					&&
				!strcmp(src_server->u.inet.port, dst_server->u.inet.port))
				return true;
			break;
		case GLUSTER_TRANSPORT__MAX:
			break;
	}

    return false;
}


static void dbus_handler_close(struct tcmu_device *dev)
{
	/* nop */
}


static void cmdproc_thread_cleanup(void *arg)
{
	struct tcmu_device *dev = arg;
	struct tcmur_handler *rhandler = tcmu_get_runner_handler(dev);

	rhandler->close(dev);
}


on_dbus_check_config(TCMUService1 *interface,
		     GDBusMethodInvocation *invocation,
		     gchar *cfgstring,
		     gpointer user_data)
{
	char *bus_name, *obj_name;
	struct tcmur_handler *handler = user_data;
	GDBusConnection *connection;
	GError *error = NULL;
	GVariant *result;

	bus_name = g_strdup_printf("org.kernel.TCMUService1.HandlerManager1.%s",
				   handler->subtype);
	obj_name = g_strdup_printf("/org/kernel/TCMUService1/HandlerManager1/%s",
				   handler->subtype);
	connection = g_dbus_method_invocation_get_connection(invocation);
	result = g_dbus_connection_call_sync(connection,
					     bus_name,
					     obj_name,
					     "org.kernel.TCMUService1",
					     "CheckConfig",
					     g_variant_new("(s)", cfgstring),
					     NULL, G_DBUS_CALL_FLAGS_NONE, -1,
					     NULL, &error);
	if (result)
		g_dbus_method_invocation_return_value(invocation, result);
	else
		g_dbus_method_invocation_return_value(invocation,
			g_variant_new("(bs)", FALSE, error->message));
	g_free(bus_name);
	g_free(obj_name);
	return TRUE;
}


static void dbus_bus_acquired(GDBusConnection *connection,
			      const gchar *name,
			      gpointer user_data)
{
	struct tcmur_handler **handler;

	tcmu_dbg("bus %s acquired\n", name);

	manager = g_dbus_object_manager_server_new("/org/kernel/TCMUService1");

	darray_foreach(handler, g_runner_handlers) {
		dbus_export_handler(*handler, G_CALLBACK(on_check_config));
	}

	dbus_handler_manager1_init(connection);
	g_dbus_object_manager_server_set_connection(manager, connection);
}


static void *tcmur_cmdproc_thread(void *arg)
{
	struct tcmu_device *dev = arg;
	struct tcmur_handler *rhandler = tcmu_get_runner_handler(dev);
	struct pollfd pfd;
	int ret;

	pthread_cleanup_push(cmdproc_thread_cleanup, dev);

	while (1) {
                int completed = 0;
		struct tcmulib_cmd *cmd;

		tcmulib_processing_start(dev);

		while ((cmd = tcmulib_get_next_command(dev)) != NULL) {
			if (tcmu_get_log_level() == TCMU_LOG_DEBUG_SCSI_CMD)
				tcmu_cdb_debug_info(cmd);

			if (tcmur_handler_is_passthrough_only(rhandler))
				ret = tcmur_cmd_passthrough_handler(dev, cmd);
			else
				ret = tcmur_generic_handle_cmd(dev, cmd);

			if (ret == TCMU_NOT_HANDLED)
				tcmu_warn("Command 0x%x not supported\n", cmd->cdb[0]);

			/*
			 * command (processing) completion is called in the following
			 * scenarios:
			 *   - handle_cmd: synchronous handlers
			 *   - generic_handle_cmd: non tcmur handler calls (see generic_cmd())
			 *			   and on errors when calling tcmur handler.
			 */
			if (ret != TCMU_ASYNC_HANDLED) {
				completed = 1;
				tcmur_command_complete(dev, cmd, ret);
			}
		}

		if (completed)
			tcmulib_processing_complete(dev);

		pfd.fd = tcmu_get_dev_fd(dev);
		pfd.events = POLLIN;
		pfd.revents = 0;

		poll(&pfd, 1, -1);

		if (pfd.revents != POLLIN) {
			tcmu_err("poll received unexpected revent: 0x%x\n", pfd.revents);
			break;
		}
	}

	tcmu_err("thread terminating, should never happen\n");

	pthread_cleanup_pop(1);

	return NULL;
}


static uint64_t get_cluster_offset(struct qcow_state *s, const uint64_t offset, bool allocate)
{
	unsigned int l1_index;
	unsigned int l2_index;
	uint64_t l2_offset;
	uint64_t *l2_table;
	uint64_t cluster_offset;

	tcmu_dbg("%s: %"PRIx64" %s\n", __func__, offset, allocate ? "write" : "read");

	l1_index = offset >> (s->l2_bits + s->cluster_bits);
	l2_offset = be64toh(s->l1_table[l1_index]) & s->cluster_mask;
	l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
	// TODO, check refcount on L2 table and handle CoW for metadata updates
	tcmu_dbg("  l1_index = %d\n", l1_index);
	tcmu_dbg("  l2_offset = %"PRIx64"\n", l2_offset);
	tcmu_dbg("  l2_index = %d\n", l2_index);

	if (!l2_offset) {
		if (!allocate || !(l2_offset = l2_table_alloc(s)))
			return 0;
		l1_table_update(s, l1_index, l2_offset | s->cluster_copied);
		s->set_refcount(s, l2_offset, 1);
	}

	l2_table = l2_cache_lookup(s, l2_offset);
	if (!l2_table)
		return 0;

	cluster_offset = be64toh(l2_table[l2_index]); // & s->cluster_mask;
	tcmu_dbg("  l2_table @ %p\n", l2_table);
	tcmu_dbg("  cluster offset = %" PRIx64 "\n", cluster_offset);

	if (!cluster_offset) {
		/* sector not allocated in image file */
		if (!allocate || !(cluster_offset = qcow_cluster_alloc(s)))
			return 0;
		l2_table_update(s, l2_table, l2_offset, l2_index, cluster_offset | s->cluster_copied);
		s->set_refcount(s, cluster_offset, 1);
	} else if ((cluster_offset & s->cluster_compressed) && allocate) {
		tcmu_err("re-allocating compressed cluster for writing\n");
		/* reallocate a compressed cluster for writing */
		if (decompress_cluster(s, cluster_offset) < 0)
			return 0;
		if (!(cluster_offset = qcow_cluster_alloc(s)))
			return 0;
		if (pwrite(s->fd, s->cluster_cache, s->cluster_size, cluster_offset) != s->cluster_size)
			return 0;
		l2_table_update(s, l2_table, l2_offset, l2_index, cluster_offset | s->cluster_copied);
		s->set_refcount(s, cluster_offset, 1);
	} else if (!(cluster_offset & s->cluster_copied) && allocate) {
		tcmu_err("re-allocating shared cluster for writing\n");
		/* refcount > 1 (the copied bit means refcount == 1)
		 * need to make a new copy if this is for a write */
		uint64_t old_offset = cluster_offset & s->cluster_mask;
		// TODO what if this is compressed?
		uint8_t *cow_buffer;
		if (!(cow_buffer = malloc(s->cluster_size)))
			goto fail;
		if (!(cluster_offset = qcow_cluster_alloc(s)))
			goto fail;
		if (pread(s->fd, cow_buffer, s->cluster_size, old_offset) != s->cluster_size)
			goto fail;
		if (pwrite(s->fd, cow_buffer, s->cluster_size, cluster_offset) != s->cluster_size)
			goto fail;
		free(cow_buffer);
		l2_table_update(s, l2_table, l2_offset, l2_index, cluster_offset | s->cluster_copied);
		s->set_refcount(s, cluster_offset, 1);
		// TODO drop refcount on old cluster
		goto out;
	fail:
		tcmu_err("CoW failed\n");
		free(cow_buffer);
		return 0;
	}
out:
	return cluster_offset & ~(s->cluster_copied);
}


static int fbo_read(struct tcmu_device *dev, uint8_t *cdb, struct iovec *iovec,
		    size_t iov_cnt, uint8_t *sense)
{
	struct fbo_state *state = tcmu_get_dev_private(dev);
	uint8_t fua = cdb[1] & 0x08;
	uint64_t cur_lba = 0;
	uint64_t offset;
	int length = 0;
	int remaining;
	size_t ret;
	int rc;

	// TBD: If we simulate start/stop, then fail if stopped
	/* DPO and RelAdr bits should be 0 */
	if (cdb[0] != READ_6 && cdb[1] & 0x11)
		return tcmu_set_sense_data(sense, ILLEGAL_REQUEST,
					   ASC_INVALID_FIELD_IN_CDB, NULL);

	rc = fbo_check_lba_and_length(state, cdb, sense, &cur_lba, &length);
	if (rc)
		return rc;

	offset = state->block_size * cur_lba;

	if (fua) {
		rc = fsync(state->fd);
		if (rc) {
			tcmu_err("sync failed: %m\n");
			return set_medium_error(sense, ASC_READ_ERROR);
		}
	}

	pthread_mutex_lock(&state->state_mtx);
	state->cur_lba = cur_lba;
	state->flags |= FBO_DEV_IO;
	pthread_mutex_unlock(&state->state_mtx);

	remaining = length;

	while (remaining) {
		ret = preadv(state->fd, iovec, iov_cnt, offset);
		if (ret < 0) {
			tcmu_err("read failed: %m\n");
			rc = set_medium_error(sense, ASC_READ_ERROR);
			break;
		}
		tcmu_seek_in_iovec(iovec, ret);
		offset += ret;
		remaining -= ret;
	}

	pthread_mutex_lock(&state->state_mtx);
	state->flags &= ~FBO_DEV_IO;
	pthread_mutex_unlock(&state->state_mtx);

	return SAM_STAT_GOOD;
}


int main(int argc, char **argv)
{
	GMainLoop *loop;
	GIOChannel *libtcmu_gio;
	struct tcmulib_context *ctx;

	while (1) {
		int c;
		int option_index = 0;

		c = getopt_long(argc, argv, "dhV",
				long_options, &option_index);
		if (c == -1)
			break;

		switch (c) {
		case 'd':
			tcmu_set_log_level(TCMU_CONF_LOG_DEBUG);
			break;
		case 'V':
			printf("tcmu-synthesizer %s\n", TCMUR_VERSION);
			exit(1);
		default:
		case 'h':
			usage();
			exit(1);
		}
	}

	ctx = tcmulib_initialize(&syn_handler, 1);
	if (!ctx) {
		tcmu_err("tcmulib_initialize failed\n");
		exit(1);
	}
	tcmulib_register(ctx);
	/* Set up event for libtcmu */
	libtcmu_gio = g_io_channel_unix_new(tcmulib_get_master_fd(ctx));
	g_io_add_watch(libtcmu_gio, G_IO_IN, tcmulib_callback, ctx);
	loop = g_main_loop_new(NULL, FALSE);
	g_main_loop_run(loop);
	g_main_loop_unref(loop);
	return 0;
}


static int fbo_do_format(struct tcmu_device *dev, uint8_t *sense)
{
	struct fbo_state *state = tcmu_get_dev_private(dev);
	uint32_t done_blocks = 0;
	uint64_t offset = 0;
	uint8_t *buf;
	unsigned int length = 1024 * 1024;
	size_t ret;
	int rc = SAM_STAT_GOOD;

	buf = malloc(length);
	if (!buf) {
		tcmu_dbg("  malloc failed\n");
		return tcmu_set_sense_data(sense, HARDWARE_ERROR,
					   ASC_INTERNAL_TARGET_FAILURE, NULL);
	}

	pthread_cleanup_push(fbo_cleanup_buffer, buf);
	memset(buf, 0, length);

	while (done_blocks < state->num_lbas) {
		if ((state->num_lbas - done_blocks) * state->block_size <
		    length)
			length = (state->num_lbas - done_blocks) *
				state->block_size;
		ret = pwrite(state->fd, buf, length, offset);
		if (ret == -1) {
			tcmu_err("Could not write: %m\n");
			rc = set_medium_error(sense, ASC_WRITE_ERROR);
			break;
		}
		done_blocks += length / state->block_size;
		offset += length;
		if (done_blocks < state->num_lbas)
			state->format_progress = (0x10000 * done_blocks) /
				state->num_lbas;
	}

	pthread_mutex_lock(&state->state_mtx);
	state->flags &= ~FBO_FORMATTING;
	pthread_mutex_unlock(&state->state_mtx);

	free(buf);
	pthread_cleanup_pop(0);

	return rc;
}


static uint64_t get_refcount(unsigned int order, void *rcblock, size_t index)
{
	switch (order) {
	case 0:
		return (((uint8_t *)rcblock)[index / 8] >> (index % 8)) & 0x1;
	case 1:
		return (((uint8_t *)rcblock)[index / 4] >> (2 * (index % 4))) & 0x3;
	case 2:
		return (((uint8_t *)rcblock)[index / 2] >> (4 * (index % 2))) & 0xf;
	case 3:
		return ((uint8_t *)rcblock)[index];
	case 4:
		return be16toh(((uint16_t *)rcblock)[index]);
	case 5:
		return be32toh(((uint32_t *)rcblock)[index]);
	case 6:
		return be64toh(((uint64_t *)rcblock)[index]);
	default:
		assert(0);
	}

	return 0;	/* NOT REACHED */
}


static int rc_table_update(struct qcow_state *s, unsigned int rc_index, uint64_t refblock_offset)
{
	ssize_t ret;

	tcmu_dbg("%s: setting RC[%d] to %llx\n", __func__, rc_index, refblock_offset);
	s->refcount_table[rc_index] = htobe64(refblock_offset);

	ret = pwrite(s->fd,
		&s->refcount_table[rc_index],
		sizeof(uint64_t),
		s->refcount_table_offset + (rc_index * sizeof(uint64_t)));

	if (ret != sizeof(uint64_t))
		tcmu_err("%s: error, RC writeback failed (%zd)\n", __func__, ret);

	fdatasync(s->fd);
	return ret;
}


static void tcp_tx_timestamp(struct sock *sk, u16 tsflags, struct sk_buff *skb)
{
	if (tsflags && skb) {
		struct skb_shared_info *shinfo = skb_shinfo(skb);
		struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);

		sock_tx_timestamp(sk, tsflags, &shinfo->tx_flags);
		if (tsflags & SOF_TIMESTAMPING_TX_ACK)
			tcb->txstamp_ack = 1;
		if (tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK)
			shinfo->tskey = TCP_SKB_CB(skb)->seq + skb->len - 1;
	}
}


static char *skip_int(char *start, int *out_n)
{
    char *s;
    char c;

    *out_n = 0;

    s = skip_white(start);
    if (s == NULL) {
        return NULL;
    }
    start = s;

    while (*s) {
        if (!isdigit(*s)) {
            break;
        }
        ++s;
    }
    c = *s;
    *s = 0;
    *out_n = atoi(start);
    *s = c;
    return s;
}


OPJ_BOOL opj_tcd_copy_tile_data(opj_tcd_t *p_tcd,
                                OPJ_BYTE * p_src,
                                OPJ_UINT32 p_src_length)
{
    OPJ_UINT32 i, j, l_data_size = 0;
    opj_image_comp_t * l_img_comp = 00;
    opj_tcd_tilecomp_t * l_tilec = 00;
    OPJ_UINT32 l_size_comp, l_remaining;
    OPJ_UINT32 l_nb_elem;

    l_data_size = opj_tcd_get_encoded_tile_size(p_tcd);
    if (l_data_size != p_src_length) {
        return OPJ_FALSE;
    }

    l_tilec = p_tcd->tcd_image->tiles->comps;
    l_img_comp = p_tcd->image->comps;
    for (i = 0; i < p_tcd->image->numcomps; ++i) {
        l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
        l_remaining = l_img_comp->prec & 7;  /* (%8) */
        l_nb_elem = (OPJ_UINT32)((l_tilec->x1 - l_tilec->x0) * (l_tilec->y1 -
                                 l_tilec->y0));

        if (l_remaining) {
            ++l_size_comp;
        }

        if (l_size_comp == 3) {
            l_size_comp = 4;
        }

        switch (l_size_comp) {
        case 1: {
            OPJ_CHAR * l_src_ptr = (OPJ_CHAR *) p_src;
            OPJ_INT32 * l_dest_ptr = l_tilec->data;

            if (l_img_comp->sgnd) {
                for (j = 0; j < l_nb_elem; ++j) {
                    *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
                }
            } else {
                for (j = 0; j < l_nb_elem; ++j) {
                    *(l_dest_ptr++) = (*(l_src_ptr++)) & 0xff;
                }
            }

            p_src = (OPJ_BYTE*) l_src_ptr;
        }
        break;
        case 2: {
            OPJ_INT32 * l_dest_ptr = l_tilec->data;
            OPJ_INT16 * l_src_ptr = (OPJ_INT16 *) p_src;

            if (l_img_comp->sgnd) {
                for (j = 0; j < l_nb_elem; ++j) {
                    *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
                }
            } else {
                for (j = 0; j < l_nb_elem; ++j) {
                    *(l_dest_ptr++) = (*(l_src_ptr++)) & 0xffff;
                }
            }

            p_src = (OPJ_BYTE*) l_src_ptr;
        }
        break;
        case 4: {
            OPJ_INT32 * l_src_ptr = (OPJ_INT32 *) p_src;
            OPJ_INT32 * l_dest_ptr = l_tilec->data;

            for (j = 0; j < l_nb_elem; ++j) {
                *(l_dest_ptr++) = (OPJ_INT32)(*(l_src_ptr++));
            }

            p_src = (OPJ_BYTE*) l_src_ptr;
        }
        break;
        }

        ++l_img_comp;
        ++l_tilec;
    }

    return OPJ_TRUE;
}


static OPJ_BOOL opj_tcd_t2_encode(opj_tcd_t *p_tcd,
                                  OPJ_BYTE * p_dest_data,
                                  OPJ_UINT32 * p_data_written,
                                  OPJ_UINT32 p_max_dest_size,
                                  opj_codestream_info_t *p_cstr_info)
{
    opj_t2_t * l_t2;

    l_t2 = opj_t2_create(p_tcd->image, p_tcd->cp);
    if (l_t2 == 00) {
        return OPJ_FALSE;
    }

    if (! opj_t2_encode_packets(
                l_t2,
                p_tcd->tcd_tileno,
                p_tcd->tcd_image->tiles,
                p_tcd->tcp->numlayers,
                p_dest_data,
                p_data_written,
                p_max_dest_size,
                p_cstr_info,
                p_tcd->tp_num,
                p_tcd->tp_pos,
                p_tcd->cur_pino,
                FINAL_PASS)) {
        opj_t2_destroy(l_t2);
        return OPJ_FALSE;
    }

    opj_t2_destroy(l_t2);

    /*---------------CLEAN-------------------*/
    return OPJ_TRUE;
}


OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *p_j2k,
                                opj_stream_private_t *p_stream,
                                opj_event_mgr_t * p_manager
                               )
{
    (void)p_j2k;
    (void)p_stream;
    (void)p_manager;
    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_read_cbd(opj_j2k_t *p_j2k,
                                 OPJ_BYTE * p_header_data,
                                 OPJ_UINT32 p_header_size,
                                 opj_event_mgr_t * p_manager
                                )
{
    OPJ_UINT32 l_nb_comp, l_num_comp;
    OPJ_UINT32 l_comp_def;
    OPJ_UINT32 i;
    opj_image_comp_t * l_comp = 00;

    /* preconditions */
    assert(p_header_data != 00);
    assert(p_j2k != 00);
    assert(p_manager != 00);

    l_num_comp = p_j2k->m_private_image->numcomps;

    if (p_header_size != (p_j2k->m_private_image->numcomps + 2)) {
        opj_event_msg(p_manager, EVT_ERROR, "Crror reading CBD marker\n");
        return OPJ_FALSE;
    }

    opj_read_bytes(p_header_data, &l_nb_comp,
                   2);                           /* Ncbd */
    p_header_data += 2;

    if (l_nb_comp != l_num_comp) {
        opj_event_msg(p_manager, EVT_ERROR, "Crror reading CBD marker\n");
        return OPJ_FALSE;
    }

    l_comp = p_j2k->m_private_image->comps;
    for (i = 0; i < l_num_comp; ++i) {
        opj_read_bytes(p_header_data, &l_comp_def,
                       1);                  /* Component bit depth */
        ++p_header_data;
        l_comp->sgnd = (l_comp_def >> 7) & 1;
        l_comp->prec = (l_comp_def & 0x7f) + 1;

        if (l_comp->prec > 31) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Invalid values for comp = %d : prec=%u (should be between 1 and 38 according to the JPEG2000 norm. OpenJpeg only supports up to 31)\n",
                          i, l_comp->prec);
            return OPJ_FALSE;
        }
        ++l_comp;
    }

    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_read_coc(opj_j2k_t *p_j2k,
                                 OPJ_BYTE * p_header_data,
                                 OPJ_UINT32 p_header_size,
                                 opj_event_mgr_t * p_manager
                                )
{
    opj_cp_t *l_cp = NULL;
    opj_tcp_t *l_tcp = NULL;
    opj_image_t *l_image = NULL;
    OPJ_UINT32 l_comp_room;
    OPJ_UINT32 l_comp_no;

    /* preconditions */
    assert(p_header_data != 00);
    assert(p_j2k != 00);
    assert(p_manager != 00);

    l_cp = &(p_j2k->m_cp);
    l_tcp = (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH)
            ?
            &l_cp->tcps[p_j2k->m_current_tile_number] :
            p_j2k->m_specific_param.m_decoder.m_default_tcp;
    l_image = p_j2k->m_private_image;

    l_comp_room = l_image->numcomps <= 256 ? 1 : 2;

    /* make sure room is sufficient*/
    if (p_header_size < l_comp_room + 1) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
        return OPJ_FALSE;
    }
    p_header_size -= l_comp_room + 1;

    opj_read_bytes(p_header_data, &l_comp_no,
                   l_comp_room);                 /* Ccoc */
    p_header_data += l_comp_room;
    if (l_comp_no >= l_image->numcomps) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Error reading COC marker (bad number of components)\n");
        return OPJ_FALSE;
    }

    opj_read_bytes(p_header_data, &l_tcp->tccps[l_comp_no].csty,
                   1);                  /* Scoc */
    ++p_header_data ;

    if (! opj_j2k_read_SPCod_SPCoc(p_j2k, l_comp_no, p_header_data, &p_header_size,
                                   p_manager)) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
        return OPJ_FALSE;
    }

    if (p_header_size != 0) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading COC marker\n");
        return OPJ_FALSE;
    }
    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_write_cbd(opj_j2k_t *p_j2k,
                                  struct opj_stream_private *p_stream,
                                  struct opj_event_mgr * p_manager)
{
    OPJ_UINT32 i;
    OPJ_UINT32 l_cbd_size;
    OPJ_BYTE * l_current_data = 00;
    opj_image_t *l_image = 00;
    opj_image_comp_t * l_comp = 00;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);
    assert(p_stream != 00);

    l_image = p_j2k->m_private_image;
    l_cbd_size = 6 + p_j2k->m_private_image->numcomps;

    if (l_cbd_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
        OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
                                             p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size);
        if (! new_header_tile_data) {
            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
            p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
            opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write CBD marker\n");
            return OPJ_FALSE;
        }
        p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_cbd_size;
    }

    l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;

    opj_write_bytes(l_current_data, J2K_MS_CBD, 2);                 /* CBD */
    l_current_data += 2;

    opj_write_bytes(l_current_data, l_cbd_size - 2, 2);             /* L_CBD */
    l_current_data += 2;

    opj_write_bytes(l_current_data, l_image->numcomps, 2);          /* Ncbd */
    l_current_data += 2;

    l_comp = l_image->comps;

    for (i = 0; i < l_image->numcomps; ++i) {
        opj_write_bytes(l_current_data, (l_comp->sgnd << 7) | (l_comp->prec - 1),
                        1);           /* Component bit depth */
        ++l_current_data;

        ++l_comp;
    }

    if (opj_stream_write_data(p_stream,
                              p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_cbd_size,
                              p_manager) != l_cbd_size) {
        return OPJ_FALSE;
    }

    return OPJ_TRUE;
}


void opj_j2k_destroy(opj_j2k_t *p_j2k)
{
    if (p_j2k == 00) {
        return;
    }

    if (p_j2k->m_is_decoder) {

        if (p_j2k->m_specific_param.m_decoder.m_default_tcp != 00) {
            opj_j2k_tcp_destroy(p_j2k->m_specific_param.m_decoder.m_default_tcp);
            opj_free(p_j2k->m_specific_param.m_decoder.m_default_tcp);
            p_j2k->m_specific_param.m_decoder.m_default_tcp = 00;
        }

        if (p_j2k->m_specific_param.m_decoder.m_header_data != 00) {
            opj_free(p_j2k->m_specific_param.m_decoder.m_header_data);
            p_j2k->m_specific_param.m_decoder.m_header_data = 00;
            p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
        }
    } else {

        if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) {
            opj_free(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);
            p_j2k->m_specific_param.m_encoder.m_encoded_tile_data = 00;
        }

        if (p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
            opj_free(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer);
            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer = 00;
            p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current = 00;
        }

        if (p_j2k->m_specific_param.m_encoder.m_header_tile_data) {
            opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
            p_j2k->m_specific_param.m_encoder.m_header_tile_data = 00;
            p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
        }
    }

    opj_tcd_destroy(p_j2k->m_tcd);

    opj_j2k_cp_destroy(&(p_j2k->m_cp));
    memset(&(p_j2k->m_cp), 0, sizeof(opj_cp_t));

    opj_procedure_list_destroy(p_j2k->m_procedure_list);
    p_j2k->m_procedure_list = 00;

    opj_procedure_list_destroy(p_j2k->m_validation_list);
    p_j2k->m_procedure_list = 00;

    j2k_destroy_cstr_index(p_j2k->cstr_index);
    p_j2k->cstr_index = NULL;

    opj_image_destroy(p_j2k->m_private_image);
    p_j2k->m_private_image = NULL;

    opj_image_destroy(p_j2k->m_output_image);
    p_j2k->m_output_image = NULL;

    opj_thread_pool_destroy(p_j2k->m_tp);
    p_j2k->m_tp = NULL;

    opj_free(p_j2k);
}


static OPJ_BOOL opj_j2k_read_crg(opj_j2k_t *p_j2k,
                                 OPJ_BYTE * p_header_data,
                                 OPJ_UINT32 p_header_size,
                                 opj_event_mgr_t * p_manager
                                )
{
    OPJ_UINT32 l_nb_comp;
    /* preconditions */
    assert(p_header_data != 00);
    assert(p_j2k != 00);
    assert(p_manager != 00);

    OPJ_UNUSED(p_header_data);

    l_nb_comp = p_j2k->m_private_image->numcomps;

    if (p_header_size != l_nb_comp * 4) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading CRG marker\n");
        return OPJ_FALSE;
    }
    /* Do not care of this at the moment since only local variables are set here */
    /*
    for
            (i = 0; i < l_nb_comp; ++i)
    {
            opj_read_bytes(p_header_data,&l_Xcrg_i,2);                              // Xcrg_i
            p_header_data+=2;
            opj_read_bytes(p_header_data,&l_Ycrg_i,2);                              // Xcrg_i
            p_header_data+=2;
    }
    */
    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
                                 OPJ_BYTE * p_header_data,
                                 OPJ_UINT32 p_header_size,
                                 opj_event_mgr_t * p_manager)
{
    opj_cp_t *l_cp = 00;
    opj_tcp_t *l_tcp = 00;
    OPJ_UINT32 l_tot_len, l_num_parts = 0;
    OPJ_UINT32 l_current_part;
    OPJ_UINT32 l_tile_x, l_tile_y;

    /* preconditions */

    assert(p_j2k != 00);
    assert(p_manager != 00);

    if (! opj_j2k_get_sot_values(p_header_data, p_header_size,
                                 &(p_j2k->m_current_tile_number), &l_tot_len, &l_current_part, &l_num_parts,
                                 p_manager)) {
        opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
        return OPJ_FALSE;
    }

    l_cp = &(p_j2k->m_cp);

    /* testcase 2.pdf.SIGFPE.706.1112 */
    if (p_j2k->m_current_tile_number >= l_cp->tw * l_cp->th) {
        opj_event_msg(p_manager, EVT_ERROR, "Invalid tile number %d\n",
                      p_j2k->m_current_tile_number);
        return OPJ_FALSE;
    }

    l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
    l_tile_x = p_j2k->m_current_tile_number % l_cp->tw;
    l_tile_y = p_j2k->m_current_tile_number / l_cp->tw;

    /* Fixes issue with id_000020,sig_06,src_001958,op_flip4,pos_149 */
    /* of https://github.com/uclouvain/openjpeg/issues/939 */
    /* We must avoid reading twice the same tile part number for a given tile */
    /* so as to avoid various issues, like opj_j2k_merge_ppt being called */
    /* several times. */
    /* ISO 15444-1 A.4.2 Start of tile-part (SOT) mandates that tile parts */
    /* should appear in increasing order. */
    if (l_tcp->m_current_tile_part_number + 1 != (OPJ_INT32)l_current_part) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Invalid tile part index for tile number %d. "
                      "Got %d, expected %d\n",
                      p_j2k->m_current_tile_number,
                      l_current_part,
                      l_tcp->m_current_tile_part_number + 1);
        return OPJ_FALSE;
    }
    ++ l_tcp->m_current_tile_part_number;

#ifdef USE_JPWL
    if (l_cp->correct) {

        OPJ_UINT32 tileno = p_j2k->m_current_tile_number;
        static OPJ_UINT32 backup_tileno = 0;

        /* tileno is negative or larger than the number of tiles!!! */
        if (tileno > (l_cp->tw * l_cp->th)) {
            opj_event_msg(p_manager, EVT_ERROR,
                          "JPWL: bad tile number (%d out of a maximum of %d)\n",
                          tileno, (l_cp->tw * l_cp->th));
            if (!JPWL_ASSUME) {
                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
                return OPJ_FALSE;
            }
            /* we try to correct */
            tileno = backup_tileno;
            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
                          "- setting tile number to %d\n",
                          tileno);
        }

        /* keep your private count of tiles */
        backup_tileno++;
    };
#endif /* USE_JPWL */

    /* look for the tile in the list of already processed tile (in parts). */
    /* Optimization possible here with a more complex data structure and with the removing of tiles */
    /* since the time taken by this function can only grow at the time */

    /* PSot should be equal to zero or >=14 or <= 2^32-1 */
    if ((l_tot_len != 0) && (l_tot_len < 14)) {
        if (l_tot_len ==
                12) { /* MSD: Special case for the PHR data which are read by kakadu*/
            opj_event_msg(p_manager, EVT_WARNING, "Empty SOT marker detected: Psot=%d.\n",
                          l_tot_len);
        } else {
            opj_event_msg(p_manager, EVT_ERROR,
                          "Psot value is not correct regards to the JPEG2000 norm: %d.\n", l_tot_len);
            return OPJ_FALSE;
        }
    }

#ifdef USE_JPWL
    if (l_cp->correct) {

        /* totlen is negative or larger than the bytes left!!! */
        if (/*(l_tot_len < 0) ||*/ (l_tot_len >
                                    p_header_size)) {   /* FIXME it seems correct; for info in V1 -> (p_stream_numbytesleft(p_stream) + 8))) { */
            opj_event_msg(p_manager, EVT_ERROR,
                          "JPWL: bad tile byte size (%d bytes against %d bytes left)\n",
                          l_tot_len,
                          p_header_size);  /* FIXME it seems correct; for info in V1 -> p_stream_numbytesleft(p_stream) + 8); */
            if (!JPWL_ASSUME) {
                opj_event_msg(p_manager, EVT_ERROR, "JPWL: giving up\n");
                return OPJ_FALSE;
            }
            /* we try to correct */
            l_tot_len = 0;
            opj_event_msg(p_manager, EVT_WARNING, "- trying to adjust this\n"
                          "- setting Psot to %d => assuming it is the last tile\n",
                          l_tot_len);
        }
    };
#endif /* USE_JPWL */

    /* Ref A.4.2: Psot could be equal zero if it is the last tile-part of the codestream.*/
    if (!l_tot_len) {
        opj_event_msg(p_manager, EVT_INFO,
                      "Psot value of the current tile-part is equal to zero, "
                      "we assuming it is the last tile-part of the codestream.\n");
        p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
    }

    if (l_tcp->m_nb_tile_parts != 0 && l_current_part >= l_tcp->m_nb_tile_parts) {
        /* Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2851 */
        opj_event_msg(p_manager, EVT_ERROR,
                      "In SOT marker, TPSot (%d) is not valid regards to the previous "
                      "number of tile-part (%d), giving up\n", l_current_part,
                      l_tcp->m_nb_tile_parts);
        p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
        return OPJ_FALSE;
    }

    if (l_num_parts !=
            0) { /* Number of tile-part header is provided by this tile-part header */
        l_num_parts += p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction;
        /* Useful to manage the case of textGBR.jp2 file because two values of TNSot are allowed: the correct numbers of
         * tile-parts for that tile and zero (A.4.2 of 15444-1 : 2002). */
        if (l_tcp->m_nb_tile_parts) {
            if (l_current_part >= l_tcp->m_nb_tile_parts) {
                opj_event_msg(p_manager, EVT_ERROR,
                              "In SOT marker, TPSot (%d) is not valid regards to the current "
                              "number of tile-part (%d), giving up\n", l_current_part,
                              l_tcp->m_nb_tile_parts);
                p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
                return OPJ_FALSE;
            }
        }
        if (l_current_part >= l_num_parts) {
            /* testcase 451.pdf.SIGSEGV.ce9.3723 */
            opj_event_msg(p_manager, EVT_ERROR,
                          "In SOT marker, TPSot (%d) is not valid regards to the current "
                          "number of tile-part (header) (%d), giving up\n", l_current_part, l_num_parts);
            p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
            return OPJ_FALSE;
        }
        l_tcp->m_nb_tile_parts = l_num_parts;
    }

    /* If know the number of tile part header we will check if we didn't read the last*/
    if (l_tcp->m_nb_tile_parts) {
        if (l_tcp->m_nb_tile_parts == (l_current_part + 1)) {
            p_j2k->m_specific_param.m_decoder.m_can_decode =
                1; /* Process the last tile-part header*/
        }
    }

    if (!p_j2k->m_specific_param.m_decoder.m_last_tile_part) {
        /* Keep the size of data to skip after this marker */
        p_j2k->m_specific_param.m_decoder.m_sot_length = l_tot_len -
                12; /* SOT_marker_size = 12 */
    } else {
        /* FIXME: need to be computed from the number of bytes remaining in the codestream */
        p_j2k->m_specific_param.m_decoder.m_sot_length = 0;
    }

    p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPH;

    /* Check if the current tile is outside the area we want decode or not corresponding to the tile index*/
    if (p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec == -1) {
        p_j2k->m_specific_param.m_decoder.m_skip_data =
            (l_tile_x < p_j2k->m_specific_param.m_decoder.m_start_tile_x)
            || (l_tile_x >= p_j2k->m_specific_param.m_decoder.m_end_tile_x)
            || (l_tile_y < p_j2k->m_specific_param.m_decoder.m_start_tile_y)
            || (l_tile_y >= p_j2k->m_specific_param.m_decoder.m_end_tile_y);
    } else {
        assert(p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec >= 0);
        p_j2k->m_specific_param.m_decoder.m_skip_data =
            (p_j2k->m_current_tile_number != (OPJ_UINT32)
             p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec);
    }

    /* Index */
    if (p_j2k->cstr_index) {
        assert(p_j2k->cstr_index->tile_index != 00);
        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tileno =
            p_j2k->m_current_tile_number;
        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_tpsno =
            l_current_part;

        if (l_num_parts != 0) {
            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps =
                l_num_parts;
            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps =
                l_num_parts;

            if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
                p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
                    (opj_tp_index_t*)opj_calloc(l_num_parts, sizeof(opj_tp_index_t));
                if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
                    opj_event_msg(p_manager, EVT_ERROR,
                                  "Not enough memory to read SOT marker. Tile index allocation failed\n");
                    return OPJ_FALSE;
                }
            } else {
                opj_tp_index_t *new_tp_index = (opj_tp_index_t *) opj_realloc(
                                                   p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index,
                                                   l_num_parts * sizeof(opj_tp_index_t));
                if (! new_tp_index) {
                    opj_free(p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index);
                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index = NULL;
                    opj_event_msg(p_manager, EVT_ERROR,
                                  "Not enough memory to read SOT marker. Tile index allocation failed\n");
                    return OPJ_FALSE;
                }
                p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
                    new_tp_index;
            }
        } else {
            /*if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index)*/ {

                if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 10;
                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
                        (opj_tp_index_t*)opj_calloc(
                            p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps,
                            sizeof(opj_tp_index_t));
                    if (!p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index) {
                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 0;
                        opj_event_msg(p_manager, EVT_ERROR,
                                      "Not enough memory to read SOT marker. Tile index allocation failed\n");
                        return OPJ_FALSE;
                    }
                }

                if (l_current_part >=
                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps) {
                    opj_tp_index_t *new_tp_index;
                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps =
                        l_current_part + 1;
                    new_tp_index = (opj_tp_index_t *) opj_realloc(
                                       p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index,
                                       p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps *
                                       sizeof(opj_tp_index_t));
                    if (! new_tp_index) {
                        opj_free(p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index);
                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index = NULL;
                        p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = 0;
                        opj_event_msg(p_manager, EVT_ERROR,
                                      "Not enough memory to read SOT marker. Tile index allocation failed\n");
                        return OPJ_FALSE;
                    }
                    p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tp_index =
                        new_tp_index;
                }
            }

        }

    }

    /* FIXME move this onto a separate method to call before reading any SOT, remove part about main_end header, use a index struct inside p_j2k */
    /* if (p_j2k->cstr_info) {
       if (l_tcp->first) {
       if (tileno == 0) {
       p_j2k->cstr_info->main_head_end = p_stream_tell(p_stream) - 13;
       }

       p_j2k->cstr_info->tile[tileno].tileno = tileno;
       p_j2k->cstr_info->tile[tileno].start_pos = p_stream_tell(p_stream) - 12;
       p_j2k->cstr_info->tile[tileno].end_pos = p_j2k->cstr_info->tile[tileno].start_pos + totlen - 1;
       p_j2k->cstr_info->tile[tileno].num_tps = numparts;

       if (numparts) {
       p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(numparts * sizeof(opj_tp_info_t));
       }
       else {
       p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(10 * sizeof(opj_tp_info_t)); // Fixme (10)
       }
       }
       else {
       p_j2k->cstr_info->tile[tileno].end_pos += totlen;
       }

       p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos = p_stream_tell(p_stream) - 12;
       p_j2k->cstr_info->tile[tileno].tp[partno].tp_end_pos =
       p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos + totlen - 1;
       }*/
    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_get_end_header(opj_j2k_t *p_j2k,
                                       struct opj_stream_private *p_stream,
                                       struct opj_event_mgr * p_manager)
{
    /* preconditions */
    assert(p_j2k != 00);
    assert(p_manager != 00);
    assert(p_stream != 00);

    OPJ_UNUSED(p_manager);

    p_j2k->cstr_index->main_head_end = opj_stream_tell(p_stream);

    return OPJ_TRUE;
}


static int opj_j2k_get_default_thread_count()
{
    const char* num_threads = getenv("OPJ_NUM_THREADS");
    if (num_threads == NULL || !opj_has_thread_support()) {
        return 0;
    }
    if (strcmp(num_threads, "ALL_CPUS") == 0) {
        return opj_get_num_cpus();
    }
    return atoi(num_threads);
}


static OPJ_BOOL opj_j2k_write_mct_data_group(opj_j2k_t *p_j2k,
        struct opj_stream_private *p_stream,
        struct opj_event_mgr * p_manager)
{
    OPJ_UINT32 i;
    opj_simple_mcc_decorrelation_data_t * l_mcc_record;
    opj_mct_data_t * l_mct_record;
    opj_tcp_t * l_tcp;

    /* preconditions */
    assert(p_j2k != 00);
    assert(p_stream != 00);
    assert(p_manager != 00);

    if (! opj_j2k_write_cbd(p_j2k, p_stream, p_manager)) {
        return OPJ_FALSE;
    }

    l_tcp = &(p_j2k->m_cp.tcps[p_j2k->m_current_tile_number]);
    l_mct_record = l_tcp->m_mct_records;

    for (i = 0; i < l_tcp->m_nb_mct_records; ++i) {

        if (! opj_j2k_write_mct_record(p_j2k, l_mct_record, p_stream, p_manager)) {
            return OPJ_FALSE;
        }

        ++l_mct_record;
    }

    l_mcc_record = l_tcp->m_mcc_records;

    for (i = 0; i < l_tcp->m_nb_mcc_records; ++i) {

        if (! opj_j2k_write_mcc_record(p_j2k, l_mcc_record, p_stream, p_manager)) {
            return OPJ_FALSE;
        }

        ++l_mcc_record;
    }

    if (! opj_j2k_write_mco(p_j2k, p_stream, p_manager)) {
        return OPJ_FALSE;
    }

    return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t
        *p_stream, OPJ_UINT32 tile_no, OPJ_BOOL* p_correction_needed,
        opj_event_mgr_t * p_manager)
{
    OPJ_BYTE   l_header_data[10];
    OPJ_OFF_T  l_stream_pos_backup;
    OPJ_UINT32 l_current_marker;
    OPJ_UINT32 l_marker_size;
    OPJ_UINT32 l_tile_no, l_tot_len, l_current_part, l_num_parts;

    /* initialize to no correction needed */
    *p_correction_needed = OPJ_FALSE;

    if (!opj_stream_has_seek(p_stream)) {
        /* We can't do much in this case, seek is needed */
        return OPJ_TRUE;
    }

    l_stream_pos_backup = opj_stream_tell(p_stream);
    if (l_stream_pos_backup == -1) {
        /* let's do nothing */
        return OPJ_TRUE;
    }

    for (;;) {
        /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
        if (opj_stream_read_data(p_stream, l_header_data, 2, p_manager) != 2) {
            /* assume all is OK */
            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
                return OPJ_FALSE;
            }
            return OPJ_TRUE;
        }

        /* Read 2 bytes from buffer as the new marker ID */
        opj_read_bytes(l_header_data, &l_current_marker, 2);

        if (l_current_marker != J2K_MS_SOT) {
            /* assume all is OK */
            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
                return OPJ_FALSE;
            }
            return OPJ_TRUE;
        }

        /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
        if (opj_stream_read_data(p_stream, l_header_data, 2, p_manager) != 2) {
            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
            return OPJ_FALSE;
        }

        /* Read 2 bytes from the buffer as the marker size */
        opj_read_bytes(l_header_data, &l_marker_size, 2);

        /* Check marker size for SOT Marker */
        if (l_marker_size != 10) {
            opj_event_msg(p_manager, EVT_ERROR, "Inconsistent marker size\n");
            return OPJ_FALSE;
        }
        l_marker_size -= 2;

        if (opj_stream_read_data(p_stream, l_header_data, l_marker_size,
                                 p_manager) != l_marker_size) {
            opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
            return OPJ_FALSE;
        }

        if (! opj_j2k_get_sot_values(l_header_data, l_marker_size, &l_tile_no,
                                     &l_tot_len, &l_current_part, &l_num_parts, p_manager)) {
            return OPJ_FALSE;
        }

        if (l_tile_no == tile_no) {
            /* we found what we were looking for */
            break;
        }

        if ((l_tot_len == 0U) || (l_tot_len < 14U)) {
            /* last SOT until EOC or invalid Psot value */
            /* assume all is OK */
            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
                return OPJ_FALSE;
            }
            return OPJ_TRUE;
        }
        l_tot_len -= 12U;
        /* look for next SOT marker */
        if (opj_stream_skip(p_stream, (OPJ_OFF_T)(l_tot_len),
                            p_manager) != (OPJ_OFF_T)(l_tot_len)) {
            /* assume all is OK */
            if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
                return OPJ_FALSE;
            }
            return OPJ_TRUE;
        }
    }

    /* check for correction */
    if (l_current_part == l_num_parts) {
        *p_correction_needed = OPJ_TRUE;
    }

    if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
        return OPJ_FALSE;
    }
    return OPJ_TRUE;
}


static void opj_j2k_tcp_destroy(opj_tcp_t *p_tcp)
{
    if (p_tcp == 00) {
        return;
    }

    if (p_tcp->ppt_markers != 00) {
        OPJ_UINT32 i;
        for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
            if (p_tcp->ppt_markers[i].m_data != NULL) {
                opj_free(p_tcp->ppt_markers[i].m_data);
            }
        }
        p_tcp->ppt_markers_count = 0U;
        opj_free(p_tcp->ppt_markers);
        p_tcp->ppt_markers = NULL;
    }

    if (p_tcp->ppt_buffer != 00) {
        opj_free(p_tcp->ppt_buffer);
        p_tcp->ppt_buffer = 00;
    }

    if (p_tcp->tccps != 00) {
        opj_free(p_tcp->tccps);
        p_tcp->tccps = 00;
    }

    if (p_tcp->m_mct_coding_matrix != 00) {
        opj_free(p_tcp->m_mct_coding_matrix);
        p_tcp->m_mct_coding_matrix = 00;
    }

    if (p_tcp->m_mct_decoding_matrix != 00) {
        opj_free(p_tcp->m_mct_decoding_matrix);
        p_tcp->m_mct_decoding_matrix = 00;
    }

    if (p_tcp->m_mcc_records) {
        opj_free(p_tcp->m_mcc_records);
        p_tcp->m_mcc_records = 00;
        p_tcp->m_nb_max_mcc_records = 0;
        p_tcp->m_nb_mcc_records = 0;
    }

    if (p_tcp->m_mct_records) {
        opj_mct_data_t * l_mct_data = p_tcp->m_mct_records;
        OPJ_UINT32 i;

        for (i = 0; i < p_tcp->m_nb_mct_records; ++i) {
            if (l_mct_data->m_data) {
                opj_free(l_mct_data->m_data);
                l_mct_data->m_data = 00;
            }

            ++l_mct_data;
        }

        opj_free(p_tcp->m_mct_records);
        p_tcp->m_mct_records = 00;
    }

    if (p_tcp->mct_norms != 00) {
        opj_free(p_tcp->mct_norms);
        p_tcp->mct_norms = 00;
    }

    opj_j2k_tcp_data_destroy(p_tcp);

}