All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.scudata.dm.HashIndexTable Maven / Gradle / Ivy

Go to download

SPL(Structured Process Language) A programming language specially for structured data computing.

There is a newer version: 20241126
Show newest version
package com.scudata.dm;

import com.scudata.array.BoolArray;
import com.scudata.array.IArray;
import com.scudata.array.IntArray;
import com.scudata.common.MessageManager;
import com.scudata.common.RQException;
import com.scudata.expression.Expression;
import com.scudata.resources.EngineMessage;
import com.scudata.thread.Job;
import com.scudata.thread.MultithreadUtil;
import com.scudata.thread.ThreadPool;
import com.scudata.util.HashUtil;
import com.scudata.util.Variant;

/**
 * ???ֶ?????????
 * @author WangXiaoJun
 *
 */
public class HashIndexTable extends IndexTable {
	// ???ڴ?Ź?ϣ?????Ԫ?أ???ϣֵ??ͬ??Ԫ?????????洢
	private static class Entry {
		Object key;
		int seq; // ??Ӧ?ļ?¼??????е????
		Entry next;
		
		public Entry(Object key, int seq) {
			this.key = key;
			this.seq = seq;
		}
		
		public Entry(Object key, int seq, Entry next) {
			this.key = key;
			this.seq = seq;
			this.next = next;
		}
	}

	// ???ڶ??̴߳?????ϣ??
	private static class CreateJob extends Job {
		private HashUtil hashUtil;
		private Sequence code;
		private int field;
		private int start; // ??ʼλ?ã?????
		private int end; // ????λ?ã???????
		private boolean checkDupKey; // ?????ֵ
		
		Entry []entries; // ??hashֵ????
		
		public CreateJob(HashUtil hashUtil, Sequence code, int field, int start, int end, boolean checkDupKey) {
			this.hashUtil = hashUtil;
			this.code = code;
			this.field = field;
			this.start = start;
			this.end = end;
			this.checkDupKey = checkDupKey;
		}

		public void run() {
			HashUtil hashUtil = this.hashUtil;
			Sequence code = this.code;
			int field = this.field;
			Entry []groups = new Entry[hashUtil.getCapacity()];
			this.entries = groups;
			Object key;
			BaseRecord r;

			for (int i = start, end = this.end; i < end; ++i) {
				r = (BaseRecord)code.getMem(i);
				key = r.getNormalFieldValue(field);

				int hash = hashUtil.hashCode(key);
				for (Entry entry = groups[hash]; entry != null; entry = entry.next) {
					if (checkDupKey && Variant.isEquals(entry.key, key)) {
						MessageManager mm = EngineMessage.get();
						throw new RQException(Variant.toString(key) + mm.getMessage("engine.dupKeys"));
					}
				}
				
				groups[hash] = new Entry(key, i, groups[hash]);
			}
		}
	}
	
	// ???ڶ??̴߳?????ϣ??(????ifind)
	private static class CreateJob2 extends Job {
		private HashUtil hashUtil;
		private Sequence code;
		private int field;
		private int start; // ??ʼλ?ã?????
		private int end; // ????λ?ã???????
		
		Entry []entries; // ??hashֵ????
		
		public CreateJob2(HashUtil hashUtil, Sequence code, int field, int start, int end) {
			this.hashUtil = hashUtil;
			this.code = code;
			this.field = field;
			this.start = start;
			this.end = end;
		}

		public void run() {
			HashUtil hashUtil = this.hashUtil;
			Sequence code = this.code;
			int field = this.field;
			Entry []groups = new Entry[hashUtil.getCapacity()];
			this.entries = groups;
			Object key;
			BaseRecord r;

			for (int i = start, end = this.end; i < end; ++i) {
				r = (BaseRecord)code.getMem(i);
				key = r.getNormalFieldValue(field);

				int hash = hashUtil.hashCode(key);
				
				if (groups[hash] == null) {
					groups[hash] = new Entry(key, i, null);
				} else {
					Entry entry = groups[hash];
					while (true) {
						if (entry.next == null) {
							break;
						}
						entry = entry.next;
					}
					entry.next = new Entry(key, i, null);
				}
			}
		}
	}
	
	private Sequence code; // Դ??????ϣ????ŵ???Ԫ?ص?λ?ã???Ҫ????λ?õ?Դ??ȡԪ??
	protected HashUtil hashUtil; // ???ڼ????ϣֵ
	protected Entry []entries; // ??hashֵ????
	private boolean useMultithread; // ʹ?ö??̴߳???????
	
	public HashIndexTable(int capacity) {
		hashUtil = new HashUtil(capacity);
	}
	
	/**
	 * ??????ϣ??
	 * @param capacity ??ϣ??????
	 * @param opt ѡ?m???ö??̴߳?????ϣ??
	 */
	public HashIndexTable(int capacity, String opt) {
		hashUtil = new HashUtil(capacity);
		useMultithread = opt != null && opt.indexOf('m') != -1;
	}
	
	private HashIndexTable(Sequence code, HashUtil hashUtil, Entry []entries) {
		this.code = code;
		this.hashUtil = hashUtil;
		this.entries = entries;
	}
	
	/**
	 * ȡ??ϣ??????
	 * @return
	 */
	public int getCapacity() {
		return hashUtil.getCapacity();
	}

	/**
	 * ??????ϣ??
	 * @param code Դ????
	 */
	public void create(Sequence code) {
		this.code = code;
		HashUtil hashUtil = this.hashUtil;
		Entry []groups = new Entry[hashUtil.getCapacity()];
		this.entries = groups;
		Object key;
		Object r;
		
		for (int i = 1, len = code.length(); i <= len; ++i) {
			r = code.getMem(i);
			if (r instanceof BaseRecord) {
				key = ((BaseRecord)r).getPKValue();
			} else {
				key = r;
			}

			int hash = hashUtil.hashCode(key);
			for (Entry entry = groups[hash]; entry != null; entry = entry.next) {
				if (Variant.compare(entry.key, key, true) == 0) {
					MessageManager mm = EngineMessage.get();
					throw new RQException(Variant.toString(key) + mm.getMessage("engine.dupKeys"));
				}
			}
			
			groups[hash] = new Entry(key, i, groups[hash]);
		}
	}

	/**
	 * ?ñ???ʽ?ļ???ֵ??????????ϣ??
	 * @param code Դ????
	 * @param exp ????ʽ
	 * @param ctx
	 */
	public void create(Sequence code, Expression exp, Context ctx) {
		if (exp == null) {
			create(code);
			return;
		}

		if (code instanceof Table) {
			Table table = (Table)code;
			int f = exp.getFieldIndex(table.dataStruct());
			if (f != -1) {
				create(table, f);
				return;
			}
		}
		
		this.code = code;
		HashUtil hashUtil = this.hashUtil;
		Entry []groups = new Entry[hashUtil.getCapacity()];
		this.entries = groups;
		Object key;

		ComputeStack stack = ctx.getComputeStack();
		Current current = new Current(code);
		stack.push(current);

		try {
			for (int i = 1, len = code.length(); i <= len; ++i) {
				current.setCurrent(i);
				key = exp.calculate(ctx);

				int hash = hashUtil.hashCode(key);
				for (Entry entry = groups[hash]; entry != null; entry = entry.next) {
					if (Variant.compare(entry.key, key, true) == 0) {
						MessageManager mm = EngineMessage.get();
						throw new RQException(Variant.toString(key) + mm.getMessage("engine.dupKeys"));
					}
				}
				
				groups[hash] = new Entry(key, i, groups[hash]);
			}
		} finally {
			stack.pop();
		}
	}
	
	// ?ϲ???ϣ??
	private static void combineHashGroups(Entry []result, Entry []entries, boolean checkDupKey) {
		int len = result.length;
		for (int i = 0; i < len; ++i) {
			if (result[i] == null) {
				result[i] = entries[i];
			} else if (entries[i] != null) {
				Entry entry = entries[i];
				while (true) {
					// ?ȽϹ?ϣֵ??ͬ??Ԫ???Ƿ?ֵҲ??ͬ
					for (Entry resultEntry = result[i]; resultEntry != null; resultEntry = resultEntry.next) {
						if (checkDupKey && Variant.isEquals(entry.key, resultEntry.key)) {
							MessageManager mm = EngineMessage.get();
							throw new RQException(Variant.toString(entry.key) + mm.getMessage("engine.dupKeys"));
						}
					}
					
					if (entry.next == null) {
						entry.next = result[i];
						result[i] = entries[i];
						break;
					} else {
						entry = entry.next;
					}
				}
			}
		}
	}

	// ?ϲ???ϣ??(ifind)
	private static void combineHashGroups_i(Entry []result, Entry []entries) {
		int len = result.length;
		for (int i = 0; i < len; ++i) {
			if (result[i] == null) {
				result[i] = entries[i];
			} else if (entries[i] != null) {
				Entry entry = result[i];
				while (true) {
					
					if (entry.next == null) {
						entry.next = entries[i];
						break;
					} else {
						entry = entry.next;
					}
				}
			}
		}
	}
	
	/**
	 * ?????е?ָ???ֶ?Ϊ????????ϣ??
	 * @param code Դ????
	 * @param field ?ֶ?????
	 */
	public void create(Sequence code, int field) {
		create(code, field, true);
	}
	
	/**
	 * ?????е?ָ???ֶ?Ϊ????????ϣ??
	 * @param code Դ????
	 * @param field ?ֶ?????
	 * @param checkDupKey ??????ֵ
	 */
	public void create(Sequence code, int field, boolean checkDupKey) {
		this.code = code;
		int len = code.length();
		if (useMultithread && len > MultithreadUtil.SINGLE_PROSS_COUNT && Env.getParallelNum() > 1) {
			int threadCount = Env.getParallelNum();
			int singleCount = len / threadCount;
			CreateJob []jobs = new CreateJob[threadCount];
			ThreadPool pool = ThreadPool.newInstance(threadCount);

			try {
				for (int i = 0, start = 1; i < threadCount; ++i) {
					if (i + 1 == threadCount) {
						jobs[i] = new CreateJob(hashUtil, code, field, start, len + 1, checkDupKey);
					} else {
						jobs[i] = new CreateJob(hashUtil, code, field, start, start + singleCount, checkDupKey);
						start += singleCount;
					}
					
					pool.submit(jobs[i]);
				}
				
				for (int i = 0; i < threadCount; ++i) {
					jobs[i].join();
					
					if (entries == null) {
						entries = jobs[i].entries;
					} else {
						combineHashGroups(entries, jobs[i].entries, checkDupKey);
					}
				}
			} finally {
				pool.shutdown();
			}
		} else {
			CreateJob job = new CreateJob(hashUtil, code, field, 1, len + 1, checkDupKey);
			job.run();
			entries = job.entries;
		}
	}

	/**
	 * ?????е?ָ???ֶ?Ϊ????????ϣ?? (????ifind)
	 * @param code Դ????
	 * @param field ?ֶ?????
	 */
	public void create_i(Sequence code, int field) {
		this.code = code;
		int len = code.length();
		if (useMultithread && len > MultithreadUtil.SINGLE_PROSS_COUNT && Env.getParallelNum() > 1) {
			int threadCount = Env.getParallelNum();
			int singleCount = len / threadCount;
			CreateJob2 []jobs = new CreateJob2[threadCount];
			ThreadPool pool = ThreadPool.newInstance(threadCount);

			try {
				for (int i = 0, start = 1; i < threadCount; ++i) {
					if (i + 1 == threadCount) {
						jobs[i] = new CreateJob2(hashUtil, code, field, start, len + 1);
					} else {
						jobs[i] = new CreateJob2(hashUtil, code, field, start, start + singleCount);
						start += singleCount;
					}
					
					pool.submit(jobs[i]);
				}
				
				for (int i = 0; i < threadCount; ++i) {
					jobs[i].join();
					
					if (entries == null) {
						entries = jobs[i].entries;
					} else {
						combineHashGroups_i(entries, jobs[i].entries);
					}
				}
			} finally {
				pool.shutdown();
			}
		} else {
			CreateJob2 job = new CreateJob2(hashUtil, code, field, 1, len + 1);
			job.run();
			entries = job.entries;
		}
	}
	
	/**
	 * ?ɼ?????Ԫ?أ??Ҳ??????ؿ?
	 * @param key ??ֵ
	 */
	public Object find(Object key) {
		int hash = hashUtil.hashCode(key);
		for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
			if (Variant.compare(entry.key, key, true) == 0) {
				return code.getMem(entry.seq);
			}
		}
		
		return null; // key not found
	}

	/**
	 * ?ɼ?????Ԫ?أ??Ҳ??????ؿ?
	 * @param keys ????Ϊ1?ļ?ֵ????
	 */
	public Object find(Object []keys) {
		int hash = hashUtil.hashCode(keys[0]);
		for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
			if (Variant.compare(entry.key, keys[0], true) == 0) {
				return code.getMem(entry.seq);
			}
		}

		return null; // key not found
	}
	
	/**
	 * ?ɼ?????Ԫ????ţ??Ҳ???????0
	 * @param key ??ֵ
	 */
	public int findPos(Object key) {
		int hash = hashUtil.hashCode(key);
		for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
			if (Variant.compare(entry.key, key, true) == 0) {
				return entry.seq;
			}
		}
		
		return 0; // key not found
	}
	
	/**
	 * ?ɼ?????Ԫ????ţ??Ҳ???????0
	 * @param key ??ֵ
	 */
	public int findPos(Object[] keys) {
		int hash = hashUtil.hashCode(keys[0]);
		for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
			if (Variant.compare(entry.key, keys[0], true) == 0) {
				return entry.seq;
			}
		}

		return 0; // key not found
	}
	
	/**
	 * ?????????й??????????????????????
	 * @param exp ???˱???ʽ
	 * @param ctx
	 * @return Table ?????????ļ?¼???ɵ??????
	 */
	public Table select(Expression exp, Context ctx) {
		Sequence code = this.code;
		Entry []entries = this.entries;
		int len = code.length();
		
		int capacity = entries.length;
		Entry []resultEntries = new Entry[capacity];
		Table result = new Table(code.dataStruct(), len);
		IArray mems = result.getMems();
		int newLen = 0;
		
		ComputeStack stack = ctx.getComputeStack();
		Current current = new Current(code);
		stack.push(current);
		
		try {
			for (int i = 0; i < capacity; ++i) {
				Entry entry = entries[i];
				while (entry != null) {
					current.setCurrent(entry.seq);
					Object b = exp.calculate(ctx);
					if (Variant.isTrue(b)) {
						mems.add(current.getCurrent());
						newLen++;
						Entry prev = new Entry(entry.key, newLen);
						resultEntries[i] = prev;
						
						for (entry = entry.next; entry != null; entry = entry.next) {
							current.setCurrent(entry.seq);
							b = exp.calculate(ctx);
							if (Variant.isTrue(b)) {
								newLen++;
								mems.add(current.getCurrent());
								prev.next = new Entry(entry.key, newLen);
								prev = prev.next;
							}								
						}
						
						break;
					} else {
						entry = entry.next;
					}
				}
			}
		} finally {
			stack.pop();
		}
		
		result.trimToSize();
		HashIndexTable indexTable = new HashIndexTable(result, hashUtil, resultEntries);
		result.setIndexTable(indexTable);
		return result;
	}
	
	public int[] findAllPos(IArray keys) {
		Entry []entries = this.entries;
		HashUtil hashUtil = this.hashUtil;
		int len = keys.size();
		int[] pos = new int[len + 1];
		
		for (int i = 1; i <= len; i++) {
			int hash = hashUtil.hashCode(keys.hashCode(i));
			for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
				if (keys.isEquals(i, entry.key)) {
					pos[i] =  entry.seq;
					break;
				}
			}
		}
		
		return pos;
	}

	public int[] findAllPos(IArray[] keys) {
		return findAllPos(keys[0]);
	}

	public int[] findAllPos(IArray keys, BoolArray signArray) {
		Entry []entries = this.entries;
		HashUtil hashUtil = this.hashUtil;
		int len = keys.size();
		int[] pos = new int[len + 1];
		
		for (int i = 1; i <= len; i++) {
			if (signArray.isFalse(i)) {
				continue;
			}
			int hash = hashUtil.hashCode(keys.hashCode(i));
			for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
				if (keys.isEquals(i, entry.key)) {
					pos[i] =  entry.seq;
					break;
				}
			}
		}
		
		return pos;
	}

	public int[] findAllPos(IArray[] keys, BoolArray signArray) {
		return findAllPos(keys[0], signArray);
	}
	
	/**
	 * ???ݼ????Ҷ?Ӧ??ֵ??λ??,?????ظ???ֵ
	 * @param key ??
	 * @param out
	 */
	public void findPos(Object key, IntArray out) {
		int hash = hashUtil.hashCode(key);
		for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
			if (Variant.compare(entry.key, key, true) == 0) {
				out.addInt(entry.seq);
			}
		}
	}
	
	/**
	 * ???ݼ????Ҷ?Ӧ??ֵ??λ??,?????ظ???ֵ
	 * @param key ??
	 * @param out
	 */
	public void findPos(Object[] keys, IntArray out) {
		int hash = hashUtil.hashCode(keys[0]);
		for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
			if (Variant.compare(entry.key, keys[0], true) == 0) {
				out.addInt(entry.seq);
			}
		}
	}
	
	//ֻ??ԭ??????ֵĵ?һ????λ??
	public int[] findAllFirstPos(IArray keys) {
		Entry []entries = this.entries;
		HashUtil hashUtil = this.hashUtil;
		int len = keys.size();
		int[] pos = new int[len + 1];
		
		for (int i = 1; i <= len; i++) {
			int hash = hashUtil.hashCode(keys.hashCode(i));
			for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
				if (keys.isEquals(i, entry.key)) {
					pos[i] =  entry.seq;
					break;
				}
			}
		}
		
		return pos;
	}

	public int[] findAllFirstPos(IArray[] keys) {
		return findAllFirstPos(keys[0]);
	}

	public int[] findAllFirstPos(IArray keys, BoolArray signArray) {
		Entry []entries = this.entries;
		HashUtil hashUtil = this.hashUtil;
		int len = keys.size();
		int[] pos = new int[len + 1];
		
		for (int i = 1; i <= len; i++) {
			if (signArray.isFalse(i)) {
				continue;
			}
			int hash = hashUtil.hashCode(keys.hashCode(i));
			for (Entry entry = entries[hash]; entry != null; entry = entry.next) {
				if (keys.isEquals(i, entry.key)) {
					pos[i] =  entry.seq;
					break;
				}
			}
		}
		
		return pos;
	}

	public int[] findAllFirstPos(IArray[] keys, BoolArray signArray) {
		return findAllFirstPos(keys[0], signArray);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy