1、Segment是Buffer缓冲区存储数据的基本单位,每个Segment能存储的最大字节是8192也就是8k的数据
/** The size of all segments in bytes. */
static final int SIZE = 8192; 2、SHARE_MINIMUM是用来共享的阈值,超过1k的数据才可以进行共享
/** Segments will be shared when doing so avoids {@code arraycopy()} of this many bytes. */
static final int SHARE_MINIMUM = 1024; 3、Segment存储的数据是字节数组data
final byte[] data; 4、Segmen中有记录可读的位置的pos,每次读取数据的时候从这个下标开始
/** The next byte of application data byte to read in this segment. */
int pos; 5、Segmen中有记录可写的位置的limit,每次写数据的时候从这个下标开始
/** The first byte of available data ready to be written to. */
int limit; 6、boolean shared; 标识是否本Segment可以共享,实际就是浅拷贝,共享的是里面的data数据 7、boolean owner; 标识data是否是本Segment对象所拥有的的,副本的Segment对象只能读 8、因为Buffer数据的存储Segment的设计是采用双向链表的结构,因此就出现了Segment next;和 Segment prev;
   //两个构造函数
Segment() { this.data = new byte[SIZE];
this.owner = true;
this.shared = false;
} Segment(byte[] data, int pos, int limit, boolean shared, boolean owner) {
this.data = data;
this.pos = pos;
this.limit = limit;
this.shared = shared;
this.owner = owner;
} //浅拷贝一个Segmen对象,同时标记当前的Segment对象是可共享的、新的Segment对象owner是false
final Segment sharedCopy() {
shared = true;
return new Segment(data, pos, limit, true, false);
} //深拷贝-重建Segment,data数据也是新的
final Segment unsharedCopy() {
return new Segment(data.clone(), pos, limit, false, true);
}
  //把当前的Segment对象从链表中删除,并返回他的next,如果只有一个Segment
//也就是next==this,直接返回null
public final @Nullable Segment pop() {
Segment result = next != this ? next : null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
return result;
} //在当前Segment对象的后面新增一个next对象,并返回这个新增的Segment
public final Segment push(Segment segment) {
segment.prev = this;
segment.next = next;
next.prev = segment;
next = segment;
return segment;
}
 //把当前的Segment拆分成2个Segment对象
public final Segment split(int byteCount) {
//如果划分的长度是0或者超出了当前Segment的内容(我就存了3个长度东西,你让我划分长度是5,那我就报错)
if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException();
Segment prefix;
//如果长度超过了1字节,那么我就通过sharedCopy的浅拷贝方式创建一个新的Segment对象:此时我们两个对象的数据是相同的
//pos、limit也是相同的
if (byteCount >= SHARE_MINIMUM) {
prefix = sharedCopy();
} else {
//不超过可共享的阈值那就从池子里面去拿(典型的享元模式:可参考Handler中的Message设计模式)
prefix = SegmentPool.take();
//通过arraycopy的方式把data中的byteCount的字节从pos开始,拷贝到prefix.data中去,从0开始
System.arraycopy(data, pos, prefix.data, 0, byteCount);
}
//通过上面的方式我们就得到了一个新的Segment对象:pos可能是0也可能是当前Segment的pos,取决你的生成方式。
//新的Segment对象的limit就是pos+byteCount,表示可写的下标位置
prefix.limit = prefix.pos + byteCount;
//当前的Segment对象的pos就是pos+byteCount:因为被别人拿走了byteCount长度,limit的位置不变
pos += byteCount;
//把新的Segment对象放在prev的后面连起来,这样就形成了新的链接(prefix在当前Segment的前面,前Segment-Pre的后面)
prev.push(prefix);
//返回
return prefix;
  //整理空间:被分割的Segment或者被读取的Segment会出现pos不为0的情况,导致空间的浪费
public final void compact() {
//只有自己一个Segment这里抛出了异常,个人认为没有必要,不整理不就行了,干嘛给人家抛异常
if (prev == this) throw new IllegalStateException();
//副本没有操作权限
if (!prev.owner) return; // Cannot compact: prev isn't writable.
//需要移动的长度就是当前的有效内容的长度
int byteCount = limit - pos;
//计算下前一个Segment可用空间
int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos);
//如果前一个Segment的剩余可用空间不足以放下当前的长度直接返回,无能为力
if (byteCount > availableByteCount) return; // Cannot compact: not enough writable space.
//如果前面的Segment可用剩余的空间能放得下就把byteCount长度的字节写到pre中
writeTo(prev, byteCount);
//写完之后自己的这个Segment就可以被弹出回收了
pop();
SegmentPool.recycle(this);
} 只是把当前的Segment内容移动到pre中

  //把当前Segment中byteCount长度的字节移到sink中
public final void writeTo(Segment sink, int byteCount) {
//副本是不能进行操作的
if (!sink.owner) throw new IllegalArgumentException();
//如果sink当前可写的位置+需要移动的长度超过了Segment中存储的最大值(不一定会抛异常哦,因为pos可能不是0,也就是前面都空余的空间)
if (sink.limit + byteCount > SIZE) {
// 如果sink是共享的不能写
if (sink.shared) throw new IllegalArgumentException();
//如果整个Segment可写的空间都不足以放下byteCount长度,那就是真的放不下了:抛异常
if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException();
//虽然limt后面放不下,当时pos前面还有空间,加起来剩余的空间能放下,这时候整理下sink,进行一次移动
//从pos位置移动sink.limit - sink.pos长度当前有效内容,移动到从0开始的位置
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
//移动之后更新sink.limit的位置:向前挪了pos个长度
sink.limit -= sink.pos;
//移动之后更新sink.pos的位置:从pos向前挪了pos个位置,也就是0
sink.pos = 0;
} //整理完sink之后,或者sink.limit + byteCount能放得下byteCount个字节后,把当前Segment中的byteCount字节移动到sink中
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
//更新sink.limit的位置
sink.limit += byteCount;
//更新当前Segment的pos的位置:被拿走了了byteCount长度,所以需要后移byteCount个长度
pos += byteCount;
}

写入方法的总结:如果sink中limt之后的剩余空间能够写的下byteCount的长度直接通过arraycopy把data中的数据移动到sink的limt后面即可,然后更新sink的limit和当前Segment的pos

如果sink中limt之后的空间放不下byteCount长度der字节,就会判断sink中可用空间是否能放下,如果可以,自身进行一次移动,把pos之前的空间利用上,更新自身的pos和limt。最后在把data中数据通过arraycopy把data中的数据移动到sink的limt后面即可,然后更新sink的limit和当前Segment的pos。

  }

okio中数据存储的基本单位Segment的更多相关文章

  1. 第2/24周 页_SQL Server 中数据存储的基本单位

    上周通过探讨SQL Server如何执行一个查询奠定了基础.我也在那里提到页是8kb的缓存.今天我们对页进行进一步集中探讨,从性能调优角度挖掘出更多的细节. 页是SQL Server的基础,在SQL ...

  2. 第2周 页_SQL Server 中数据存储的基本单位

    原文:第2周 页_SQL Server 中数据存储的基本单位 上周通过探讨SQL Server如何执行一个查询奠定了基础.我也在那里提到页是8kb的缓存.今天我们对页进行进一步集中探讨,从性能调优角度 ...

  3. Android中数据存储(一)

    国庆没有给国家添堵,没有勾搭妹子,乖乖的写着自己的博客..... 本文将为大家介绍Android中数据存储的五种方式,数据存储可是非常重要的知识哦. 一,文件存储数据 ①在ROM存储数据 关于在ROM ...

  4. Java基础知识强化之IO流笔记46:IO流练习之 把文本文件中数据存储到集合中的案例

    1.  把文本文件中数据存储到集合中      需求:从文本文件中读取数据(每一行为一个字符串数据)到集合中,并遍历集合. 分析:      通过题目的意思我们可以知道如下的一些内容,      数据 ...

  5. android中数据存储

    android中数据存储     Android 中存储数据的方式有五种:SQLite数据库.文件存储.内容提供者.网络.SharedPreferences(Key----value)五种存储方式. ...

  6. Android中数据存储(四)——ContentProvider存储数据

    目录(?)[+]   当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方 ...

  7. Android中数据存储(三)——SQLite数据库存储数据

    当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方式: 1. Share ...

  8. Android中数据存储之SharedPreferences

    import android.content.Context; import android.content.SharedPreferences; import android.content.Sha ...

  9. 在AndroidStudio中数据存储第三方数据管理Bmob的使用

    ---恢复内容开始--- 在日常写代码的过程中我们比较痛苦的就是数据库的建立和使用,那么今天来介绍一下一个第三方的数据管理平台Bmonb. 一.我们首先进入Bmob的官网创建一个账号 Bome官网网址 ...

  10. Java中数据存储分配

    (1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编 译时就可以给 ...

随机推荐

  1. Linux系统管理实战-进程管理

    进程管理 了解进程 状态/生命周期 查看进程 管理进程 kill killall pkill 进程的调度 进程的nice 了解进程状态/生命周期 什么是进程? 进程的状态? 进程的生命周期? 查看进程 ...

  2. SVN报错:database is locked

    https://blog.csdn.net/k7arm/article/details/81168416 https://www.jianshu.com/p/aa9c67fcc407

  3. openstack安装部署私有云详细图文

    本文主要分享的是云计算.openstack的使用.私有云平台建设.云服务器云硬盘的构建和使用.从基本概念入手到私有云建设,信息量非常大.对于openstack的安装部署都是从官方文档中一步步的介绍,内 ...

  4. NOIP2012普及组

    T2]寻宝 读懂题目!! 是逆时针,第几个有钥匙的房间,还有能够直接上楼的是作为第一个有钥匙的房间,而不是就从这里直接上楼了 #include<iostream> #include< ...

  5. 论zzy的苏州话

    乘地铁(ceng) - 盛饭(seng) 无法无天 - 12345 掀被子 - 先干嘛,再干嘛 ?待更新

  6. 基于5G/4G智能网关的大货车安全监测方案

    大货车是我们身边最常见的货运车辆,从各种原材料到货物成品,都需要大大小小的货车承担过程中的运输工作.而由于货车通常载重多.体积大.行车盲区多,因此也产生较多的交通安全风险. 针对大货车的交通安全保障, ...

  7. CH573 CH582 CH579蓝牙主机(Central)例程讲解一(主机工作流程)

    蓝牙主机,顾名思义,就是一个蓝牙主设备,与从机建立连接进行通信,可以接收从机通知,也可以给从机发送信息,可将Central例程和Peripheral例程结合使用. 蓝牙主机例程的工作流程大致如下: 一 ...

  8. BeanFactory与FactoryBean区别

    1. BeanFactory BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂.在Spring中,BeanFactory是IOC容器的核心接口,也是 ...

  9. 学习笔记-C++

    题目:声明一个基类BaseClass,从它派生出类DerivedClass,BaseClass有成员函数fn1()和fn2(),fn1()是虚函数,DerivedClass也有成员函数fn1()和fn ...

  10. sql union 和 union all

    UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 但是需要注意: 1.union内部的select语句必须拥有相同数量的列. 2.列必须拥有相似的数据类型. 3.每条select语句 ...