`
suichangkele
  • 浏览: 200267 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

FST源代码解读4——结束添加

阅读更多

lucene fst 源代码 解读

 

前面讲到调用FST.finish方法了,进入到这个阶段的前提是所有的term都已经被编译进fst了,也就是写入到bytes中了。看下代码如下:

/** 当编译root节点之后调用,传入的是root节点的编号(或者是在bytes中的开始位置) */
void finish(long newStartNode) throws IOException {
	assert newStartNode <= bytes.getPosition();
	if (startNode != -1) {
		throw new IllegalStateException("already finished");
	}
	if (newStartNode == FINAL_END_NODE && emptyOutput != null) {
		newStartNode = 0;
	}
	startNode = newStartNode;//start节点在bytes中的开始
	bytes.finish();
	cacheRootArcs();//缓存从root发出的部分arc
}

 里面有一个cacheRootArcs方法,因为FST在查找的时候,每次必须从root出发,也就是从root出发的arc每次都要被查找,所以可以将其缓存起来,提高效率。

/** Optionally caches first 128 labels <br/> 缓存从root节点发出的lebal值小于128的多个arc,这样就根据lebal查找arc,因为从root出发的是每次查找都必须用得到的,缓存的方式是将值作为下标。 */
@SuppressWarnings({ "unchecked" })
private void cacheRootArcs() throws IOException {

	// We should only be called once per FST:
	assert cachedArcsBytesUsed == 0;

	final Arc<T> arc = new Arc<>();// 模拟一个指向root的一个arc
	getFirstArc(arc);// 获得一个虚拟的指向root的arc
	if (targetHasArcs(arc)) {// 如果这个root有输出,如果root都没有输出,则没有任何东西了。所以一定是true
		final BytesReader in = getBytesReader();// 获得用来读取bytes的对象
		Arc<T>[] arcs = (Arc<T>[]) new Arc[0x80];// 用来封装查询到的arc(不是全部查完,下面有说明)。这个arc类和builder时的arc类不一样,不是一个类
		readFirstRealTargetArc(arc.target, arc, in);// 读取第一个arc
		int count = 0;// 一共缓存了多少个
		while (true) {
			assert arc.label != END_LABEL;
			if (arc.label < arcs.length) {// 如果这个的lebal是小于128的,则缓存这个,写入到arcs中
				arcs[arc.label] = new Arc<T>().copyFrom(arc);
			} else {
				break;
			}
			if (arc.isLast()) {// 是不是所在的节点的最后一个arc
				break;
			}
			readNextRealArc(arc, in);
			count++;
		}

		int cacheRAM = (int) ramBytesUsed(arcs);

		// Don't cache if there are only a few arcs or if the cache would
		// use > 20% RAM of the FST itself:
		if (count >= FIXED_ARRAY_NUM_ARCS_SHALLOW && cacheRAM < ramBytesUsed() / 5) {
			cachedRootArcs = arcs;
			cachedArcsBytesUsed = cacheRAM;
		}
	}
}
/**
 * 读取一个节点的第一个arc的信息
 * @param node   节点(或者是位置或者是节点的编号)
 * @param arc    指向节点的arc
 * @param in     读取fst的对象
 */
public Arc<T> readFirstRealTargetArc(long node, Arc<T> arc, final BytesReader in) throws IOException {

	final long address = getNodeAddress(node);// 节点的开始位置(如果是倒序的,就从后面开始的)
	in.setPosition(address);// 定位到要读取的节点的第一个byte
	arc.node = node;

	if (in.readByte() == ARCS_AS_FIXED_ARRAY) {// 这个节点的存储格式是fixedArray,先读取一些header,也就是addNode中写的那一部分header
		// this is first arc in a fixed-array
		arc.numArcs = in.readVInt();// 这个节点发出的arc的数量
		if (packed || version >= VERSION_VINT_TARGET) {
			arc.bytesPerArc = in.readVInt();
		} else {
			arc.bytesPerArc = in.readInt();
		}
		arc.arcIdx = -1;
		arc.nextArc = arc.posArcsStart = in.getPosition();
	} else {// 如果不是fixedArray的话,没有header,位置就是第一个arc(也就是nextArc)的开始位置
		// arc.flags = b;
		arc.nextArc = address;
		arc.bytesPerArc = 0;
	}
	// 上面是定位第一个arc的开始,下面开始读了
	return readNextRealArc(arc, in);
	
}
/** 读取一个arc <br/> Never returns null, but you should never call this if arc.isLast() is true. */
public Arc<T> readNextRealArc(Arc<T> arc, final BytesReader in) throws IOException {
	// TODO: can't assert this because we call from readFirstArc
	// assert !flag(arc.flags, BIT_LAST_ARC);

	// this is a continuing arc in a fixed array
	if (arc.bytesPerArc != 0) {// fixedArray的格式
		// arcs are at fixed entries
		arc.arcIdx++;// 这个在设置第一个arc的时候是-1,所以第一个arc的话这里是0
		assert arc.arcIdx < arc.numArcs;
		in.setPosition(arc.posArcsStart);// 采用的策略是先定位到这个节点的第一个arc的开始,然后下面再根据arc的下标定位到某一个arc的开始。
		in.skipBytes(arc.arcIdx * arc.bytesPerArc);// 倒退指定的bytes,开始读取这个
	} else {// 如果
		// arcs are packed
		in.setPosition(arc.nextArc);// 定位到开始
	}

	// flag和label是一定有的,其他的属性是否有要根据flag来判断。
	arc.flags = in.readByte();
	arc.label = readLabel(in);

	if (arc.flag(BIT_ARC_HAS_OUTPUT)) {// 是否有输出
		arc.output = outputs.read(in);
	} else {
		arc.output = outputs.getNoOutput();
	}

	if (arc.flag(BIT_ARC_HAS_FINAL_OUTPUT)) {// 有final输出
		arc.nextFinalOutput = outputs.readFinalOutput(in);
	} else {
		arc.nextFinalOutput = outputs.getNoOutput();
	}

	// 上面已经读取完了这个arc的属性,下面是读取这个arc的target node

	if (arc.flag(BIT_STOP_NODE)) {// 是指向最后一个节点,也就是这个arc是一个线路中的最后一个,也就是他已经构成一个线路了,不需要再读取下一个节点
		if (arc.flag(BIT_FINAL_ARC)) {// 是final的
			arc.target = FINAL_END_NODE;
		} else {// TODO 这个一直不懂,是什么鬼,已经指向最后一个node了为啥不是final的?
			arc.target = NON_FINAL_END_NODE;
		}
		arc.nextArc = in.getPosition();
	} else if (arc.flag(BIT_TARGET_NEXT)) {// 该arc的目标节点就是fst中的下一个节点,此时不用存储target,两个的node是有联系的,可以计算出来
		arc.nextArc = in.getPosition();
		// TODO: would be nice to make this lazy -- maybe
		// caller doesn't need the target and is scanning arcs...
		if (nodeAddress == null) {// 不是null,不看
			if (!arc.flag(BIT_LAST_ARC)) {
				if (arc.bytesPerArc == 0) {
					// must scan
					seekToNextNode(in);
				} else {
					in.setPosition(arc.posArcsStart);
					in.skipBytes(arc.bytesPerArc * arc.numArcs);
				}
			}
			arc.target = in.getPosition();
		} else {
			arc.target = arc.node - 1;// nodeAddress不是null,说明arc.node是编号,又因为是BIT_TARGET_NEXT,说明是连续存储的,arc.node-1就是下一个节点的编号
			assert arc.target > 0;
		}
	} else {// 需要单独的查找下一个节点
		if (packed) {// 是压缩的fst
			final long pos = in.getPosition();
			final long code = in.readVLong();
			if (arc.flag(BIT_TARGET_DELTA)) {//记录的是差值的,用当前的位置+差值(到目前为止还没有出现过,但是后面会有,经过对FST的压缩之后就会有这个)
				// Address is delta-coded from current address:
				arc.target = pos + code;
			} else if (code < nodeRefToAddress.size()) {//记录的是编号,从另一个对象中获得这个编号的节点的开始位置。之前在写入的时候加上nodeRefToAddress.size()的目的就是这个。
				// Deref
				arc.target = nodeRefToAddress.get((int) code);
			} else {
				// Absolute  记录的是绝对值
				arc.target = code;
			}
		} else {
			// 读取下一个节点的编号
			arc.target = readUnpackedNodeTarget(in);
		}
		arc.nextArc = in.getPosition();
	}

	return arc;
}

这样就缓存完了前128个arc。

这里又有一个新的Arc,这个和构建FST的时候 不是一个类,是另一个类的arc,代码如下:

/** 用于读FST时的arc <br/> Represents a single arc. */
public static final class Arc<T> {
	/** 这个arc的标记,也就是路径上的值 */
	public int label;
	public T output;

	// From node (ord or address); currently only used when building an FST w/ willPackFST=true:
	/** 或者是fst中节点的编号,或者是fst中节点的开始位置,通过编号也可以获得位置 */
	long node;

	/** 指向的node<br/> To node (ord or address) */
	public long target;

	/** 这个arc的flag,标识他都是有哪些属性 */
	byte flags;
	public T nextFinalOutput;

	// address (into the byte[]), or ord/address if label == END_LABEL
	/** 同一个节点的下一个arc的开始 */
	long nextArc;

	/**
	 * 在使用fixedArray的时候,第一个arc的开始 <br/>
	 * Where the first arc in the array starts; only valid if bytesPerArc !=
	 * 0
	 */
	public long posArcsStart;

	/**
	 * 如果不是0 表示使用fixedBytes的格式,一个arc使用的byte的数量;是0表示不是fixedBytes的格式 <br/>
	 * Non-zero if this arc is part of an array, which means all arcs for
	 * the node are encoded with a fixed number of bytes so that we can
	 * random access by index. We do when there are enough arcs leaving one
	 * node. It wastes some bytes but gives faster lookups.
	 */
	public int bytesPerArc;

	/**
	 * 当使用fixedArray的格式的时候使用 <br/>
	 * Where we are in the array; only valid if bytesPerArc != 0.
	 */
	public int arcIdx;

	/** 发出这个arc的节点一共有多少个arc <br/> How many arcs in the array; only valid if bytesPerArc != 0. */
	public int numArcs;

	/** Returns this 复制一个 */
	public Arc<T> copyFrom(Arc<T> other) {
		node = other.node;
		label = other.label;
		target = other.target;
		flags = other.flags;
		output = other.output;
		nextFinalOutput = other.nextFinalOutput;
		nextArc = other.nextArc;
		bytesPerArc = other.bytesPerArc;
		if (bytesPerArc != 0) {
			posArcsStart = other.posArcsStart;
			arcIdx = other.arcIdx;
			numArcs = other.numArcs;
		}
		return this;
	}

	/** 这个arc的flag是否有指定的属性 */
	boolean flag(int flag) {
		return FST.flag(flags, flag);
	}
};

 

经过这样就完成了所有的term的添加,形成了一个fst,最终存储在一个叫做BytesStore的对象中,不过在存储的时候是倒叙的,如果要读,必须要从后向前读,代码如下

/** 获得一个读取byteStore的对象 <br/> Returns a {@link BytesReader} for this FST, positioned at position 0. */
public BytesReader getBytesReader() {
	if (packed) {// 如果是经过压缩的,则是正向的,否则是反向的
		if (bytesArray != null) {
			return new ForwardBytesReader(bytesArray);
		} else {
			return bytes.getForwardReader();
		}
	} else {
		if (bytesArray != null) {// 建立的时候不用,读的时候,如果fst不是很大才会用
			return new ReverseBytesReader(bytesArray);
		} else {
			return bytes.getReverseReader();
		}
	}
}

 现在还没有说过压缩fst,所以都是进入的return new ReverseBytesReader(bytesArray);也就是生成一个倒叙读的reader。下一篇博客将介绍如何对FST做压缩。 

 如果读者发现我写的有问题,请联系我的QQ:1308567317

分享到:
评论

相关推荐

    Lucene中的FST算法描述

    例如,如果一个FST图表示的term及其对应的输出值为"stop"对应4,那么可以将这个关系图解为如下步骤:从起始节点开始,选择标记为's'的弧线,到达节点23,并带上权重3;继续选择标记为't'的弧线,到达节点21,权重为0...

    fst-2.57-API文档-中英对照版.zip

    赠送源代码:fst-2.57-sources.jar; 赠送Maven依赖信息文件:fst-2.57.pom; 包含翻译后的API文档:fst-2.57-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:de.ruedigermoeller:fst:2.57; 标签:...

    FST:快速Java序列化的替代品

    1. **添加依赖**:将FST库引入到项目中,通常通过Maven或Gradle等构建工具进行管理。 2. **注册序列化器**:如果需要序列化的类没有实现`Serializable`接口,可以使用FST的注册机制,指定自定义的序列化和反序列化...

    74FST3253 高速4选1数据手册

    根据提供的文件内容,这里是一份关于74FST3253高速4选1数据手册的知识点整理: 1. 器件介绍: 74FST3253是由ON Semiconductor生产的高速4选1多路选择器/解复用器总线开关。这款器件在4到5.5伏的电压范围内与CMOS ...

    Go-FST有限状态变换器的一个Go库实现

    源代码通常包括示例、测试用例和文档,这些都对理解库的用法和实现原理非常有帮助。 总的来说,Go库"vellum"为开发人员提供了一种强大而灵活的工具,用于在Go环境中处理有限状态变换器。通过深入研究这个库,我们...

    fst-2.57-API文档-中文版.zip

    赠送源代码:fst-2.57-sources.jar; 赠送Maven依赖信息文件:fst-2.57.pom; 包含翻译后的API文档:fst-2.57-javadoc-API文档-中文(简体)版.zip; Maven坐标:de.ruedigermoeller:fst:2.57; 标签:ruedigermoeller...

    帧差法改进matlab源代码

    ### 帧差法改进Matlab源代码解析与详解 #### 一、帧差法基本原理及应用背景 帧差法是一种广泛应用于视频处理领域的技术,主要用于运动目标检测。其核心思想是通过比较相邻帧之间的差异来判断物体的移动情况。这种...

    FST610变频器说明书.pdf

    FST610变频器是深圳市佛斯特科技有限公司生产的一款高性能矢量通用型变频器,它支持多种控制方式,如VF控制、无PG矢量控制、转矩控制,拥有丰富的参数功能,适用于对速度、转矩响应速度、低频力矩要求较高的场合。...

    关于Lucene的词典FST深入剖析-申艳超1

    《关于Lucene的词典FST深入剖析》这篇文章是由申艳超撰写的,主要探讨了Apache Lucene这个全文搜索引擎库中的一个关键数据结构——有限状态转换器(Finite State Transducer,简称FST)。FST在Lucene中被用于构建和...

    fst2.4及其依赖包

    FST2.4版本的使用并不复杂,首先,你需要将对应的jar包添加到项目的类路径中。考虑到描述中提到的maven下载困难,可以手动下载并引入到本地仓库,或者直接在项目中引用提供的jar包。FST库的使用一般包括以下几个步骤...

    AES.zip_AES_AES源代码_aes c语言_c++ aes加密解密_c语言 源代码

    提供的源代码可以作为学习AES加密算法的宝贵资源,也可以直接应用于实际项目中,提供加密和解密的功能。为了确保代码安全,开发者应遵循良好的编程实践,如输入验证、错误处理和安全的内存管理,同时要注意避免常见...

    fst-1.60.zip

    jaque.zip,Java查询库使语言级代码表达式在运行时以表达式树的形式表示为对象,

    MSN 样式的按钮和进度条控件[MSNstylebuttonprogress.rar]-精品源代码

    这篇文档将深入探讨"MSN 样式的按钮和进度条控件"这一主题,这些控件是基于源代码设计的,可以用于创建具有MSN风格的用户界面。在编程领域,特别是GUI(图形用户界面)设计中,自定义控件是提升应用界面独特性和用户...

    佛斯特FST-500系列变频器说明书.rar

    佛斯特FST-500系列变频器是一款广泛应用在工业自动化领域的设备,它通过调节电机的供电频率来实现电机速度的控制,从而达到节能、调速的目的。本说明书主要涵盖了该系列变频器的基本原理、功能特性、安装调试、操作...

    fst 配置文件读取

    在IT领域,配置文件的管理和读取是系统初始化和运行中的关键步骤,特别是在软件开发、运维自动化...通过阅读README.TXT,你可以获得关于如何使用和解读“fst”配置文件的具体步骤和示例,从而更好地应用到实际工作中。

    FST 4_festosoftware_

    【FST 4_festosoftware_】是一个与Festo公司相关的软件包,版本为4.01。Festo是一家全球知名的自动化技术供应商,尤其在气动和电气自动化领域享有盛誉。他们的软件通常用于控制、模拟、设计以及优化自动化设备和系统...

    FST_for_Mobile_LE_1209.pdf

    - **风河添加的功能**:提供额外的测试工具和特性。 综上所述,风河自动化软件测试框架(FST)是一款高度集成、自动化程度极高的测试解决方案,特别适用于基于Linux系统的移动设备制造商。通过FST的帮助,企业不仅...

    FST文件生产工具

    可根据seed值及dat数据,计算fst文件,可用于软件加密狗的复制等。

    FST650变频器说明书.pdf

    FST650变频器说明书,包括安装,配线,接线端子图,操作说明,详细功能讲解,故障检查与排除,保养和维护,通信协议,以及功能参数简表等技术资料。

Global site tag (gtag.js) - Google Analytics