Stats-LikeR

 view release on metacpan or  search on metacpan

LikeR.xs  view on Meta::CPAN

/* Append an unsigned integer's decimal text to an SV. */
static void xlsx_cat_uint(pTHX_ SV *restrict b, unsigned long v) {
	char tmp[24];
	int n = snprintf(tmp, sizeof(tmp), "%lu", v);
	if (n > 0) sv_catpvn(b, tmp, (STRLEN)n);
}

/* Append s (UTF-8 bytes) to out, escaping XML metacharacters and dropping the
 * control characters XML 1.0 forbids (all but tab / newline / carriage-return). */
static void xlsx_xml_cat(pTHX_ SV *restrict out, const char *restrict s, STRLEN len) {
	for (STRLEN i = 0; i < len; i++) {
		unsigned char c = (unsigned char)s[i];
		switch (c) {
		case '&':  SV_CATLIT(out, "&amp;");  break;
		case '<':  SV_CATLIT(out, "&lt;");   break;
		case '>':  SV_CATLIT(out, "&gt;");   break;
		case '"':  SV_CATLIT(out, "&quot;"); break;
		case '\'': SV_CATLIT(out, "&apos;"); break;
		default:
			if (c < 0x20 && c != '\t' && c != '\n' && c != '\r') break;
			sv_catpvn(out, (const char*)&s[i], 1);
		}
	}
}

/* 0-based column index -> A1-style letters (A, B, ..., Z, AA, ...) appended. */
static void xlsx_col_letters(pTHX_ SV *restrict b, size_t idx) {
	char tmp[16];
	int n = 0;
	size_t v = idx + 1;			/* bijective base-26 */
	while (v > 0 && n < (int)sizeof(tmp)) {
		v -= 1;
		tmp[n++] = (char)('A' + (int)(v % 26));
		v /= 26;
	}
	while (n > 0) { char c = tmp[--n]; sv_catpvn(b, &c, 1); }
}

/* True when a cell should be written as an xlsx number: looks_like_number and
 * made only of the characters a plain/scientific decimal uses, so "Inf"/"NaN"
 * and space-padded values fall back to text and never produce an invalid <v>. */
static bool xlsx_plain_number(pTHX_ SV *restrict cell) {
	if (!cell || !SvOK(cell) || !looks_like_number(cell)) return 0;
	STRLEN l; const char *restrict s = SvPV(cell, l);
	if (l == 0) return 0;
	for (STRLEN i = 0; i < l; i++) {
		char c = s[i];
		if (!((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E'
				|| c == '+' || c == '-')) return 0;
	}
	return 1;
}

/* Append one STORED (uncompressed) member to the ZIP under construction: 'zip'
 * is the growing archive, 'cdir' accumulates its central-directory records and
 * '*count' the member count. */
static void xlsx_zip_add(pTHX_ SV *restrict zip, SV *restrict cdir,
	unsigned *restrict count, const char *restrict name, SV *restrict content)
{
	STRLEN nlen = strlen(name);
	STRLEN clen; const char *restrict cdata = SvPV(content, clen);
	uint32_t crc = xlsx_crc32((const unsigned char*)cdata, (size_t)clen);
	uint32_t off = (uint32_t)SvCUR(zip);
	/* local file header */
	zip_le32(aTHX_ zip, 0x04034b50);
	zip_le16(aTHX_ zip, 20);		/* version needed to extract */
	zip_le16(aTHX_ zip, 0);			/* general-purpose flags */
	zip_le16(aTHX_ zip, 0);			/* method 0 = stored */
	zip_le16(aTHX_ zip, 0);			/* mod time */
	zip_le16(aTHX_ zip, 0x21);		/* mod date = 1980-01-01 */
	zip_le32(aTHX_ zip, crc);
	zip_le32(aTHX_ zip, (uint32_t)clen);	/* compressed size */
	zip_le32(aTHX_ zip, (uint32_t)clen);	/* uncompressed size */
	zip_le16(aTHX_ zip, (unsigned)nlen);
	zip_le16(aTHX_ zip, 0);			/* extra length */
	sv_catpvn(zip, name, nlen);
	sv_catpvn(zip, cdata, clen);
	/* central-directory header */
	zip_le32(aTHX_ cdir, 0x02014b50);
	zip_le16(aTHX_ cdir, 20);		/* version made by */
	zip_le16(aTHX_ cdir, 20);		/* version needed */
	zip_le16(aTHX_ cdir, 0);
	zip_le16(aTHX_ cdir, 0);
	zip_le16(aTHX_ cdir, 0);
	zip_le16(aTHX_ cdir, 0x21);
	zip_le32(aTHX_ cdir, crc);
	zip_le32(aTHX_ cdir, (uint32_t)clen);
	zip_le32(aTHX_ cdir, (uint32_t)clen);
	zip_le16(aTHX_ cdir, (unsigned)nlen);
	zip_le16(aTHX_ cdir, 0);		/* extra length */
	zip_le16(aTHX_ cdir, 0);		/* comment length */
	zip_le16(aTHX_ cdir, 0);		/* disk number start */
	zip_le16(aTHX_ cdir, 0);		/* internal attributes */
	zip_le32(aTHX_ cdir, 0);		/* external attributes */
	zip_le32(aTHX_ cdir, off);		/* local-header offset */
	sv_catpvn(cdir, name, nlen);
	(*count)++;
}

/* Build a complete .xlsx from the collected rows (element 0 = header record,
 * the rest data records, each an AV of SVs -- exactly what print_string_row()
 * gathers for the tex path) and write it to 'file'. freeze_rows / freeze_cols
 * give the number of leading rows / columns to freeze in place (0 = none). */
static void write_xlsx_workbook(pTHX_ AV *restrict rows, const char *restrict file,
	const char *restrict sheet_name, SV *restrict comment,
	unsigned freeze_rows, unsigned freeze_cols)
{
	/* ---- worksheet ---- */
	SV *restrict sheet = sv_2mortal(newSVpvs(
		"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
		"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"));
	/* Freeze panes: a <sheetViews> block, which the schema requires *before*
	 * <sheetData>. topLeftCell is the first cell below/right of the frozen
	 * region -- e.g. freezing 1 row gives "A2"; 1 row + 2 cols gives "C2". */
	if (freeze_rows || freeze_cols) {
		SV *restrict tl = sv_2mortal(newSVpvs(""));
		xlsx_col_letters(aTHX_ tl, (size_t)freeze_cols);
		xlsx_cat_uint(aTHX_ tl, (unsigned long)freeze_rows + 1);
		STRLEN tll; const char *restrict tls = SvPV(tl, tll);
		const char *restrict ap = (freeze_rows && freeze_cols) ? "bottomRight"
			: freeze_cols ? "topRight" : "bottomLeft";
		SV_CATLIT(sheet, "<sheetViews><sheetView workbookViewId=\"0\"><pane ");
		if (freeze_cols) {
			SV_CATLIT(sheet, "xSplit=\"");
			xlsx_cat_uint(aTHX_ sheet, (unsigned long)freeze_cols);
			SV_CATLIT(sheet, "\" ");
		}
		if (freeze_rows) {
			SV_CATLIT(sheet, "ySplit=\"");
			xlsx_cat_uint(aTHX_ sheet, (unsigned long)freeze_rows);
			SV_CATLIT(sheet, "\" ");
		}
		SV_CATLIT(sheet, "topLeftCell=\"");
		sv_catpvn(sheet, tls, tll);
		SV_CATLIT(sheet, "\" activePane=\"");
		sv_catpv(sheet, ap);
		SV_CATLIT(sheet, "\" state=\"frozen\"/><selection pane=\"");



( run in 2.090 seconds using v1.01-cache-2.11-cpan-6aa56a78535 )