asm {
//************************************
SYS_HASH_STR::
// IN:	RSI = Addr of string
// OUT: RAX
				XOR 		RAX, RAX
				TEST		RSI, RSI
				JZ			@@15

				PUSH		RSI
				PUSH		RBX
				XOR 		RBX, RBX
				JMP 		@@10

@@05:			SHL1		RBX
				ADC 		RBX, RAX
@@10:			LODSB
				TEST		AL, AL
				JNZ 		@@05

				MOV 		RAX, RBX
				SHR 		RBX, 16
				ADC 		RAX, RBX
				POP 		RBX
				POP 		RSI

@@15:			RET

//************************************
SYS_HASH_SINGLE_TABLE_FIND1::
// IN:	RAX = HASHED STRING VAL
//		RSI = STR
//		RBX = TYPE MASK
//		RDI = TABLE
//		RCX = INSTANCE, NOT ZERO
// OUT: RAX = ENTRY OR ZERO NOT FOUND
//		RDX = POINTER TO POINTER TO ENTRY
//		RCX IF NOT FOUND ENOUGH, DECREMENTED BY NUM MATCHES
//		ZERO FLAG SET NOT FOUND
				MOV 		RCX, 1
SYS_HASH_SINGLE_TABLE_FIND::
				TEST		RCX, RCX
				JNZ 		@@05
				XOR 		RAX, RAX
				RET
@@05:			AND 		RAX, U64 CHashTable.mask[RDI]
				MOV 		RDX, U64 CHashTable.body[RDI]
				LEA 		RDX, U64 [RDX + RAX * 8]
@@10:			MOV 		RAX, U64 [RDX]
				TEST		RAX, RAX
				JNZ 		@@15
				RET

@@15:			TEST		U32 CHash.type[RAX], EBX
				JZ			@@30
				PUSH		RAX
				PUSH		RDI
				PUSH		RSI
				MOV 		RDI, U64 CHash.str[RAX]
@@20:			LODSB
				CMP 		U8 [RDI], AL
				JNE 		@@25
				INC 		RDI
				TEST		AL, AL
				JNZ 		@@20
				POP 		RSI
				POP 		RDI
				POP 		RAX
				LOOP		@@30
				INC 		U32 CHash.use_count[RAX]
				TEST		RAX, RAX
				RET

@@25:			POP 		RSI
				POP 		RDI
				POP 		RAX

@@30:			LEA 		RDX, U64 CHash.next[RAX]
				JMP 		@@10

//************************************
SYS_HASH_FIND1::
// IN:	RSI = STR
//		RBX = TYPE MASK
//		RDI = TABLE
//		RCX = INSTANCE NUM
// OUT: RAX = ENTRY OR ZERO NOT FOUND
//		ZERO FLAG SET NOT FOUND
				MOV 		RCX, 1
SYS_HASH_FIND::
				PUSH		RDI
				CALL		SYS_HASH_STR
 
@@05:			PUSH		RAX
				CALL		SYS_HASH_SINGLE_TABLE_FIND
				JNZ 		@@15
				POP 		RAX
@@10:			MOV 		RDI, U64 CHashTable.next[RDI]
				TEST		RDI, RDI
				JNZ 		@@05
				POP 		RDI
				XOR 		RAX, RAX
				RET

@@15:			ADD 		RSP, 8
				POP 		RDI
				TEST		RAX, RAX
				RET

//************************************
SYS_HASH_BUCKET_FIND::
// IN:	RSI = STR
//		RDI = TABLE
// OUT: RAX = BUCKET
				PUSH		RDX
				CALL		SYS_HASH_STR
				AND 		RAX, U64 CHashTable.mask[RDI]
				MOV 		RDX, U64 CHashTable.body[RDI]
				LEA 		RAX, U64 [RDX+RAX * 8]
				POP 		RDX
				RET
_HASH_STR::
				PUSH		RBP
				MOV 		RBP, RSP
				PUSH		RSI
				MOV 		RSI, U64 SF_ARG1[RBP]
				CALL		SYS_HASH_STR
				POP 		RSI
				POP 		RBP
				RET1		8
_HASH_FIND::
				PUSH		RBP
				MOV 		RBP, RSP
				PUSH		RSI
				PUSH		RDI
				MOV 		RSI, U64 SF_ARG1[RBP]
				MOV 		RDI, U64 SF_ARG2[RBP]
				MOV 		RBX, U64 SF_ARG3[RBP]
				MOV 		RCX, U64 SF_ARG4[RBP]
				CALL		SYS_HASH_FIND
				POP 		RDI
				POP 		RSI
				POP 		RBP
				RET1		32
_HASH_SINGLE_TABLE_FIND::
				PUSH		RBP
				MOV 		RBP, RSP
				PUSH		RSI
				PUSH		RDI
				MOV 		RSI, U64 SF_ARG1[RBP]
				MOV 		RDI, U64 SF_ARG2[RBP]
				MOV 		RBX, U64 SF_ARG3[RBP]
				MOV 		RCX, U64 SF_ARG4[RBP]
				CALL		SYS_HASH_STR
				CALL		SYS_HASH_SINGLE_TABLE_FIND
				POP 		RDI
				POP 		RSI
				POP 		RBP
				RET1		32
_HASH_BUCKET_FIND::
				PUSH		RBP
				MOV 		RBP, RSP
				PUSH		RSI
				PUSH		RDI
				MOV 		RSI, U64 SF_ARG1[RBP]
				MOV 		RDI, U64 SF_ARG2[RBP]
				CALL		SYS_HASH_BUCKET_FIND
				POP 		RDI
				POP 		RSI
				POP 		RBP
				RET1		16
_HASH_ADD::
				PUSH		RBP
				MOV 		RBP, RSP
				PUSH		RSI
				PUSH		RDI
				MOV 		RCX, U64 SF_ARG1[RBP]
				MOV 		RSI, U64 CHash.str[RCX]
				MOV 		RDI, U64 SF_ARG2[RBP]
				CALL		SYS_HASH_BUCKET_FIND
				MOV 		RCX, U64 SF_ARG1[RBP]
				PUSHFD
				CLI
				MOV 		RBX, U64 [RAX]
				MOV 		U64 CHash.next[RCX], RBX
				MOV 		U64 [RAX], RCX

				POPFD
				POP 		RDI
				POP 		RSI
				POP 		RBP
				RET1		16
_HASH_ADD_AFTER::
				PUSH		RBP
				MOV 		RBP, RSP
				PUSH		RDI
				MOV 		RCX, U64 SF_ARG1[RBP]
				MOV 		RDI, U64 SF_ARG3[RBP]
				PUSHFD
				CLI
				MOV 		RAX, SF_ARG2[RBP]
				MOV 		RBX, U64 [RAX]
				MOV 		U64 CHash.next[RCX], RBX
				MOV 		U64 [RAX], RCX

				POPFD
				POP 		RDI
				POP 		RBP
				RET1		24
_HASH_REM_DEL::
				PUSH		RBP
				MOV 		RBP, RSP
				PUSH		RSI
				PUSH		RDI
				MOV 		RCX, U64 SF_ARG1[RBP]
				TEST		RCX, RCX
				JZ			@@10
				MOV 		RSI, U64 CHash.str[RCX]
				XOR 		RBX, RBX
				MOV 		EBX, U32 CHash.type[RCX]
				AND 		EBX, ~HTG_FLAGS_MASK & 0xFFFFFFFF
				MOV 		RDI, U64 SF_ARG2[RBP]
				MOV 		RCX, U64 SF_ARG3[RBP]
				CALL		SYS_HASH_STR

				PUSHFD
				CLI
				CALL		SYS_HASH_SINGLE_TABLE_FIND
				JZ			@@05
				CMP 		RAX, U64 SF_ARG1[RBP]
				JNE 		@@05

				MOV 		RBX, U64 CHash.next[RAX]
				MOV 		U64 [RDX], RBX

				POPFD

				PUSH_C_REGS
				PUSH		RAX
				CALL		&HashDel
				POP_C_REGS

				POP 		RDI
				POP 		RSI
				MOV 		RAX, 1
				POP 		RBP
				RET1		24

@@05:			POPFD
@@10:			POP 		RDI
				POP 		RSI
				XOR 		RAX, RAX
				POP 		RBP
				RET1		24
}

// Hash a string.
_extern _HASH_STR				I64		HashStr(U8 *st);
// Find string in hash table.
_extern _HASH_FIND				CHash  *HashFind(U8 *needle_str, CHashTable *haystack_table, I64 mask, I64 instance=1);
// Find string in single hash table.
_extern _HASH_SINGLE_TABLE_FIND	CHash  *HashSingleTableFind(U8 *needle_str, CHashTable *haystack_table,
															I64 mask, I64 instance=1);
// Find hash bucket.
_extern _HASH_BUCKET_FIND		CHash **HashBucketFind(U8 *needle_str, CHashTable *haystack_table);
// Add entry to hash table.
_extern _HASH_ADD				U0		HashAdd(CHash *tmph, CHashTable *table);
// Remove hash entry and del. Instance must match.
_extern _HASH_REM_DEL			Bool	HashRemDel(CHash *tmph, CHashTable *table, I64 instance=1);
