#include "ext2_meth.h"

#if 0 /* Simple, understandable, maintainable version.
	 Maybe someone will prefer this, and remove the
	 more efficient version currently in the #else. */

/*
 *	Some predefined compression method names.
 */

static struct { char const name[8]; unsigned id; } const name2id[] = {
  { ""		, ~0u			},
  { "auto"	, EXT2FS_AUTO_METH	},
  { "bzip"	, EXT2FS_BZIP2_METH	},
  { "bzip2"	, EXT2FS_BZIP2_METH	},
  { "defer"	, EXT2FS_DEFER_METH	},
  { "gzip"	, EXT2FS_GZIP_6_METH	},
  { "gzip1"	, EXT2FS_GZIP_1_METH	},
  { "gzip2"	, EXT2FS_GZIP_2_METH	},
  { "gzip3"	, EXT2FS_GZIP_3_METH	},
  { "gzip4"	, EXT2FS_GZIP_4_METH	},
  { "gzip5"	, EXT2FS_GZIP_5_METH	},
  { "gzip6"	, EXT2FS_GZIP_6_METH	},
  { "gzip7"	, EXT2FS_GZIP_7_METH	},
  { "gzip8"	, EXT2FS_GZIP_8_METH	},
  { "gzip9"	, EXT2FS_GZIP_9_METH	},
  { "lzo"	, EXT2FS_LZO1X_1_METH	},
  { "lzo1x1"	, EXT2FS_LZO1X_1_METH	},
  { "lzrw3a"	, EXT2FS_LZRW3A_METH	},
  { "lzv"	, EXT2FS_LZV1_METH	},
  { "lzv1"	, EXT2FS_LZV1_METH	},
  { "never"	, EXT2FS_NEVER_METH	},
  { "none"	, EXT2FS_DEFER_METH	},
};

unsigned
meth_name2id(char const *name)
{
	unsigned i;
	int cmp;

	if (name[0] >= 'a')
		for (i = sizeof(name2id) / sizeof(*name2id) - 1;; i--) {
			if ((cmp = strcmp (name, name2id[i].name)) > 0)
				continue;
			else if (cmp == 0)
				return name2id[i].id;
			else
				break;
		}
	return ~0u;
}

#else /* More efficient version.  (Typically better than binary search.) */
typedef unsigned u32;

/* Unfortunately, the obvious `(*(u32 const *) "blah")' doesn't compile.
   But maybe that would be relying too much on compiler alignment
   decisions anyway. */
# define hash(c1,c2,c3,c4) (((u32) (c1) << 24) \
			   | ((u32) (c2) << 16) \
			   | ((u32) (c3) << 8) \
			   | ((u32) (c4)))

unsigned meth_name2id(char const *name)
{
	/* Needed so that we never access illegal memory. */
	if ((name[0] == '\0') || (name[1] == '\0') || (name[2] == '\0'))
		return ~0u;

	switch (hash(name[0], name[1], name[2], name[3])) {
		case hash('n', 'e', 'v', 'e'):
			if ((name[4] == 'r') && (name[5] == '\0'))
				return EXT2FS_NEVER_METH;
			break;
		case hash('d', 'e', 'f', 'e'):
			if ((name[4] == 'r') && (name[5] == '\0'))
				return EXT2FS_DEFER_METH;
			break;
		case hash('n', 'o', 'n', 'e'):
			if (name[4] == '\0')
				return EXT2FS_DEFER_METH;
			break;
		case hash('a', 'u', 't', 'o'):
			if (name[4] == '\0')
				return EXT2FS_AUTO_METH;
			break;
		case hash('l', 'z', 'o', '\0'):
			return EXT2FS_LZO1X_1_METH;
		case hash('l', 'z', 'o', '1'):
			if ((name[4] == 'x') && (name[5] == '1')
				    && (name[6] == '\0'))
				return EXT2FS_LZO1X_1_METH;
			break;
		case hash('l', 'z', 'v', '\0'):
			return EXT2FS_LZV1_METH;
		case hash('l', 'z', 'v', '1'):
			if (name[4] == '\0')
				return EXT2FS_LZV1_METH;
			break;
		case hash('l', 'z', 'r', 'w'):
			if ((name[4] == '3') && (name[5] == 'a')
				    && (name[6] == '\0'))
				return EXT2FS_LZRW3A_METH;
			break;
		case hash('b', 'z', 'i', 'p'):
			if (((name[4] == '2') && (name[5] == '\0'))
				    || (name[4] == '\0'))
				return EXT2FS_BZIP2_METH;
			break;
		case hash('g', 'z', 'i', 'p'):
			if (name[4] == '\0')
				return EXT2FS_GZIP_6_METH;
# if EXT2FS_GZIP_9_METH - EXT2FS_GZIP_1_METH != 8
#  error "Bad assumption here."
# endif
			if ((name[4] >= '1') && (name[4] <= '9')
				    && (name[5] == '\0'))
				return EXT2FS_GZIP_1_METH + name[4] - '1';
			break;
		default:
			break;
	}
	return ~0u;
}
#endif

static char const meth_name[32][7] =
{
  "lzv1",   "auto",  "defer", "never",
  "bzip2",  "0x05",  "0x06",  "0x07",
  "lzrw3a", "0x09",  "lzo1x1","0x0b",
  "0x0c",   "0x0d",  "0x0e",  "0x0f",
  "gzip1",  "gzip2", "gzip3", "gzip4",
  "gzip5",  "gzip6", "gzip7", "gzip8",
  "gzip9",  "0x19",  "0x1a",  "0x1b",
  "0x1c",   "0x1d",  "0x1e",  "0x1f"
};

#if EXT2FS_LZV1_METH != 0 || EXT2FS_AUTO_METH != 1 || EXT2FS_DEFER_METH != 2 || EXT2FS_NEVER_METH != 3
# error "method id mismatch row 1"
#endif
#if EXT2FS_BZIP2_METH != 4 || EXT2FS_LZRW3A_METH != 8
# error "method id mismatch row 2--3"
#endif
#if EXT2FS_GZIP_1_METH != 16 || EXT2FS_GZIP_2_METH != 17 || EXT2FS_GZIP_9_METH != 24
# error "method id mismatch row 5--7"
#endif

char const *meth_id2name (unsigned method)
{
	if (method < 32)
		return meth_name[method];
	else
		return "*inval";
}
