From 4f7be92b031758fbf1c6ef802fa5a8294bc401e6 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Fri, 17 Nov 2023 20:29:41 -0500 Subject: [PATCH] fuck your cum assholes --- .../ChunkedCompressedChecksumFileReader.java | 74 ++++++++++++------ src/main/java/server/Connection.java | 23 +++--- src/main/java/server/Server.java | 2 +- src/main/java/shared/FileUtil.java | 5 +- src/main/java/shared/OTelUtils.java | 6 +- .../ChunkedCompressedChecksumFileWriter.class | Bin 3702 -> 3070 bytes .../ChunkedCompressedChecksumFileReader.class | Bin 5700 -> 6071 bytes target/classes/server/Connection.class | Bin 4354 -> 4325 bytes .../FileUtil$InvalidUsageException.class | Bin 472 -> 472 bytes target/classes/shared/FileUtil.class | Bin 5682 -> 6061 bytes 10 files changed, 72 insertions(+), 38 deletions(-) diff --git a/src/main/java/server/ChunkedCompressedChecksumFileReader.java b/src/main/java/server/ChunkedCompressedChecksumFileReader.java index b27c77b..58e0d99 100644 --- a/src/main/java/server/ChunkedCompressedChecksumFileReader.java +++ b/src/main/java/server/ChunkedCompressedChecksumFileReader.java @@ -2,16 +2,13 @@ package server; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import net.jpountz.xxhash.StreamingXXHash64; -import shared.ArrayData; import shared.FileUtil; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; public class ChunkedCompressedChecksumFileReader { @@ -20,6 +17,14 @@ public class ChunkedCompressedChecksumFileReader { private final DataOutputStream fileOutputWriter; private final long seed; + private Span currentSpan = null; + private Scope currentScope = null; + private long count = 0; + private static final long MAX_COUNT = 50; + + private long uncompressed_bytes = 0; + private long compressed_bytes = 0; + public ChunkedCompressedChecksumFileReader(DataInputStream networkStreamReader, String fileOutputPath, long seed) throws IOException { this.networkStreamReader = networkStreamReader; this.streamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(seed); @@ -28,26 +33,36 @@ public class ChunkedCompressedChecksumFileReader { } public FileHeader readChunk(Tracer trace, Span sp) throws IOException { - Span gf = trace.spanBuilder("Chunk Read").startSpan(); - FileHeader header = readHeader(); - gf.setAttribute("Read Uncompressed", header.getUncompressed()); - gf.setAttribute("Read Compressed", header.getCompressed()); - gf.setAttribute("Read Hash", header.getHash()); - try (Scope scope = gf.makeCurrent()) { - if (header.getUncompressed() == 0) - return header; - gf.addEvent("Read Data"); - byte[] data = readSome(header); - gf.addEvent("Decompress Data"); - byte[] decompressed = decompress(header, data); - gf.addEvent("Hash"); - hash(header, decompressed); - gf.addEvent("Write"); - fileOutputWriter.write(decompressed, 0, decompressed.length); - gf.addEvent("End"); - } finally { - gf.end(); + if (++count >= MAX_COUNT) { + currentSpan.addEvent("--{End Read}--"); + currentScope.close(); + currentSpan.end(); + currentSpan = null; + currentScope = null; } + if (currentSpan == null) { + count = 0; + currentSpan = trace.spanBuilder("Chunk Read").startSpan(); + currentScope = currentSpan.makeCurrent(); + } + FileHeader header = readHeader(); + uncompressed_bytes += header.getUncompressed(); + compressed_bytes += header.getCompressed(); + currentSpan.addEvent("--{Begin Read}--"); + currentSpan.addEvent("Attribute: Read Uncompressed = " + header.getUncompressed()); + currentSpan.addEvent("Attribute: Read Compressed = " + header.getCompressed()); + currentSpan.addEvent("Attribute: Compression Ratio = " + ((double)header.getUncompressed() / header.getCompressed())); + currentSpan.addEvent("Attribute: Read Hash = " + header.getHash()); + if (header.getUncompressed() == 0) + return header; + currentSpan.addEvent("Read Data"); + byte[] data = readSome(header); + currentSpan.addEvent("Decompress Data"); + byte[] decompressed = decompress(header, data); + currentSpan.addEvent("Hash"); + hash(header, decompressed); + currentSpan.addEvent("Write"); + fileOutputWriter.write(decompressed, 0, decompressed.length); return header; } @@ -57,6 +72,21 @@ public class ChunkedCompressedChecksumFileReader { throw new RuntimeException("Stream total hash doesn't match the client's sent hash!"); fileOutputWriter.flush(); fileOutputWriter.close(); + currentSpan.addEvent("--{End Read}--"); + currentScope.close(); + currentSpan.end(); + } + + public long getCompressedBytes(){ + return compressed_bytes; + } + + public long getUncompressedBytes(){ + return uncompressed_bytes; + } + + public double getRatio(){ + return (double) uncompressed_bytes / (double) compressed_bytes; } private FileHeader readHeader() throws IOException { diff --git a/src/main/java/server/Connection.java b/src/main/java/server/Connection.java index 9ece3cc..06d4172 100644 --- a/src/main/java/server/Connection.java +++ b/src/main/java/server/Connection.java @@ -51,19 +51,20 @@ public class Connection implements Runnable { } try { if (in.available() > 0) { - fileSend.addEvent("File Received"); - Span fileIn = trace.spanBuilder("File Received").setAttribute("Files Received", filesReceived).startSpan(); - try (Scope s = fileIn.makeCurrent()){ - byte command = in.readByte(); + byte command = in.readByte(); - if (command == FileUtil.COMMAND.CLOSE.type) { - System.out.println("Client sent disconnect signal!"); - break; - } - if (command == FileUtil.COMMAND.WRITE.type) + if (command == FileUtil.COMMAND.CLOSE.type) { + System.out.println("Client sent disconnect signal!"); + break; + } + if (command == FileUtil.COMMAND.WRITE.type) { + fileSend.addEvent("File Received"); + Span fileIn = trace.spanBuilder("File Received").setAttribute("Files Received", filesReceived).startSpan(); + try (Scope s = fileIn.makeCurrent()) { FileUtil.receive(in, trace, fileIn); - } finally { - fileIn.end(); + } finally { + fileIn.end(); + } } } } catch (IOException e) { diff --git a/src/main/java/server/Server.java b/src/main/java/server/Server.java index 4a04e58..1a85bdf 100644 --- a/src/main/java/server/Server.java +++ b/src/main/java/server/Server.java @@ -24,7 +24,7 @@ public class Server { public static volatile boolean running = true; - private static final OpenTelemetry ot = OTelUtils.create(); + private static final OpenTelemetry ot = OTelUtils.create("CumServer"); public Server() { Tracer main = ot.getTracer("Main Server", "0.69"); diff --git a/src/main/java/shared/FileUtil.java b/src/main/java/shared/FileUtil.java index b2a1c2d..e0523c0 100644 --- a/src/main/java/shared/FileUtil.java +++ b/src/main/java/shared/FileUtil.java @@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit; public class FileUtil { // do not change it breaks stuff - protected static final int READER_SIZE = 131072; + protected static final int READER_SIZE = 65000; public static final long SEED = 691; private static final LZ4Factory LZ_FACTORY = LZ4Factory.fastestInstance(); @@ -77,6 +77,9 @@ public class FileUtil { break; } } + sp.setAttribute("Data Read Uncompressed Bytes", reader.getUncompressedBytes()); + sp.setAttribute("Data Read Compressed Bytes", reader.getCompressedBytes()); + sp.setAttribute("Data Compression Ratio", reader.getRatio()); reader.close(); System.out.println("Writing " + path + " complete"); sp.addEvent("File Written"); diff --git a/src/main/java/shared/OTelUtils.java b/src/main/java/shared/OTelUtils.java index f4af919..f7cfcb2 100644 --- a/src/main/java/shared/OTelUtils.java +++ b/src/main/java/shared/OTelUtils.java @@ -39,8 +39,8 @@ public class OTelUtils { .buildAndRegisterGlobal(); } - public static OpenTelemetry create(){ - Resource resource = Resource.getDefault().toBuilder().put(ResourceAttributes.SERVICE_NAME.getKey(), "cum").put(ResourceAttributes.SERVICE_VERSION.getKey(), "0.1.0").build(); + public static OpenTelemetry create(String name){ + Resource resource = Resource.getDefault().toBuilder().put(ResourceAttributes.SERVICE_NAME.getKey(), name).put(ResourceAttributes.SERVICE_VERSION.getKey(), "1.3.37").build(); SpanExporter otlpExporter = OtlpGrpcSpanExporter.builder() .setEndpoint("http://sc.on.underlying.skynet.tpgc.me:4317") @@ -49,7 +49,7 @@ public class OTelUtils { BatchSpanProcessor batchSpanProcessor = BatchSpanProcessor.builder(otlpExporter) .setMaxQueueSize(2048) - .setMaxExportBatchSize(512) // Example max export batch size + .setMaxExportBatchSize(512) .build(); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() diff --git a/target/classes/client/ChunkedCompressedChecksumFileWriter.class b/target/classes/client/ChunkedCompressedChecksumFileWriter.class index 18420d62888db09bf4602edc8bc550f48162b3c9..2cb0de65f93079a4d9d6166ebc093e06f773f565 100644 GIT binary patch delta 1334 zcmY+E`)?Fg6vscaGuzpny;T>LcGoSovC_wO+Y;L)G?pSQ%0fj&L|c4q+l5w_wzgZq z=i&nu6&1!8^-)CyHJV5vgOR5GWK2x_cZ?>Ao;y3Kb&@+b_uTJ2-|zXJJM(+$N{#dD zl^>UZUcNc+{1R$($g)Lo8*K*mfkJ5@U&=f5Ghnk#aXWVy)Sf(1>z2*yHuaNJ?4ZgZ zyuUa%JFanqiaXh55IH57A>#YdEeHST$BhCipO}|U9eKdL3hcjt1n_|In#>kUrWys3DLw=a~cnvD2q183Wj(ZY0TVrp{M=2Q1u`>eYd?T(9P=}QDD zg_H6=<^B=fXt-{tbJNxD$WE8UVLk8io;%`Xu6>^>LY$J=McL7No82=`i;OrK@uXR(YK45B z4+Zy;AVlb2#B4t16Ioe;T#)hEil{xpWysDt&2D<^)U%9)$6X%x=~X!p74k)`@ddO? z;ttl*NgW$}VXBfQ5cVHgNThEL95^Us+JS^ldqY5$3g!l()3vXr=@OdmGS^7YYT3BT zfJDF}xx|1*pF1ICH_CAx(^i@1DjYr?rV;LTZa4fv;o{;_H6_0(#|+ delta 2002 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 diff --git a/target/classes/server/Connection.class b/target/classes/server/Connection.class index 09208c487ff0c5ea354264e1564423c024720e8e..f493d39fb2368e76092d0dd2211baac9816eb8b6 100644 GIT binary patch delta 1037 zcmYL{TWnNS6o$Vwotb@R&S@zvmK=jftHHvwp~jfPgqAX-NJA?+wpNLW8QTfPmLd&; z#D}J+4@O_ik;|Y~P^6$J*r_8U>O9GdCPss|U~IkM?Ukn*OcHNtP*%Sx6(h$n!%Ge?^NO(4n=wnh%VuRwj#nLy zb3zF3J@A;aC1vrtS83JO`!jDiobm@Ez3Gnbc8k+O`R1KN*%j$*Uv}rdY`?{u-k7Pj z&+@k69fxzA_YPa5w#_7Y!<55&-g+x>i{X8t`q9k3%$lLh@QyXz10%clXC4{K#wk#A z_<)Pv+E}8}W`vI%J|?Qcv%~#fUo2O7G0qL-{m0YoM)G+lUN2s&-DH`*-Me;WhWqnF z_VUnZ8)4!g+gDF9SY*G@S=OT16nHYnjH2=aW5=kNr`}_h}u8E48!`b zv@p((oY2_QEa7W@QsnR(Kl2MA;^J^sZ$w4IdixEULbhwf?^N@9T~AQ`4~}6{mjBI4 znWKlO!SWkoczi?w3JP;nV*bDI``QhztXV=GdYHvd8A^Y#q%#;Or)+-UKVoDS*{X-~ aUPE(OolHCinqX7rGk0+$=b^3_})Dw+6?yqGe$c zFB*+r7;Yp7KZf$9Vr3}eHY-J?(FE?+i;6pVPrE`aaM5y#MF_{N6V` z7h0dW`NW8^!Q@3Dv}SlT*V2<4%nk3%4VkN8w{y;H zb~Xj=nS8LxQyjoY0A$;;Sw<>M2;JTe&5bfoP4c&9p)ybJBRtKs4bPX^UE+H!-ce@H z0V-}&!`=!{c<9%Bv72{#Xn3jVxe8l+invwC#B07u-+bRIcl#s$XvFlX7E{tEn^=LDFV*chFTAc8CVo4cIxS7Rw~IojB+m%+^0;PIzO$m zI&{)14w6#7P9e0j6#h<<=Dj43HoS(?gr#B8(ljN4ELJUr(qR;#qinS@IumK=JSYh?85Dk) zAB_HR#$Q+26sw?8K?-d&-2_BYkxjt`#eGLb#SJ$oo_kxCAGS01+V~@kNPc%bQ}!WUM||wJH@$ zy0Hdp4Xnd@g0CT#y3#{A5bR@0W_r+wjRw+qlrTvg)AMPj7~ve9-b4soPCxyBk~bT8 zLMAsk`DMFGtz$ExK!snlSc!OWPBdjDB(A3nJdI}vI)^Hy!cr}&B zPsS`TVS}Y(2f>(OHO7LqO;#!ui)+})nkCVd(TJ6ZNQ)b-Xfu!lL*6n5p2rIWXY5`p znM$fkcWCGkhqMrNiL+W{couEa^F;%@rKiVAR$EQ6MX5y7Djj>pbbV@IAAurn6yg;F z`|&CvC%L@QN)d`>&%`dh(D}N7B{(3C>J`3L95ir9Rb95U-fBu}I3kK%Mf9eqbQO6I z%X-K0wuW~Myo>il+%?bnzJU*LQXFuN)sN#N10UlP(d#OAerDivd?6~_<5>Tdfv@q6 zSm~bTJZ0cJd@pvp%RQ&@qlTYks-FdUYMj3q_!Ym2`JQt9H2yGf7JrJho`~~r1OMRP z%puP{8x0o^<=#i5gpoHZx6DmrDWcIe%z(y;#d-S&T}#NF8;jRl@rK}H*=^EkqNwx^ z_D|-C%hpQ^1?AQ?#!@kz%0s zoJW<0LJeyQFJK1jS{lPN8m94w%T2CxzzvTafST1B?w}k_xCgCuJg;4|O0#I+hygvw z)|s6h=-$hUo(=tqJyIb@+D9?d-3yZkt^}H`JjKE@)qCpWVP`T6L{sFl#ii#-6 zr6p1qZ08Wr(CTGG4e<3N>S4kTHs2C5U&`X;S)tO>J&yYE=AbMF&z3070iDPTcjGmH z@z#_nygn9hDAB@^aj360jZhn< zp_)e0C~mTkcW|`aD2^CSS1}2@B9RN~MWseptB$uqr{YWB!L4dIKzd9kj%9G79VfE* zFoRFk<;x7d?ZgimoN33IEPl`6FWFd9n^T~6)X%8`$ioo+O&f;c{O_PK>|>)63bAxD voGd0#Dc=KZ3v={%bxu&{Bz0aFr0bP~9TOPe4O9Ibq|=T3)m2a>O@seGJFR%# delta 1506 zcmYjQdr*{B6#t!lec$eWz_P3YtLPFMuz)+2Mbsc>$kc>Ris_Iu#Re;VJQjn+!9RkT zm}&aETalE}+tMsxbwMc5vX^Dq!_4fRJ+v3SUx=N1m$AN?z31HDJ@=g7`Q5X-L&L?c zLo;I&0LrOo$hF;SwE*#**2C6yF7#r(7Z#X0?jaaeW=kZnq1}wdB2f+ZGSU>@5)PSd zA$j6LANmd0VMyfPv@nf=F>M(Z}(kftT>IxYAwbc-6omye4jQ zU%>cb18?9>G48&?@s@$N@s6+RM_7&w6sMZZy|AH~N8 zKEbErgb{LlZr}@?jC)cJSZo?0_fBn|m`bZ7L#**sr_OazCS_@q&4Q3$?D8C3IiHYP z6NxsM(Z;|g`C6pY0)kgQn#8zQ3124h!2fUx)VH;6Zi&PqI-Mu>q*vJT35$g%JyWE5 zJya+bdP{r0#8)7E&68rmRmjFSICXA+HT+T(KFgLOD9cSN%dIKPt*~|Q9lKgj^De|Y zVrvhMfQHzy;M>_CAOqjAZzKbd_C0>!trI_DilNiur1y4?7A+aes#M@I&bkDp_z6G5 zid8%{zu;HcVDj%b{LUB<#UJ>St@QrF-<-#T8}JWf7EFt&j6xsIU`D0~sFD-fIka1p z_%@bi+Y%7NxTRzacL51(DfZsI56u>A*a3ZY$tX4sDW;w1LM<1QeXM{-obna+)L{); zuoj)Tj!$c_73**-)}N!Tqg4^J@{NmdmKCva%zw1IiMT2Gb(4jxs!liA$S!F!8i4H# zR%po5(3^D@^I_GHqme`7s-&T()&>{cN=m7EFVo|HIf}>8SIf$8t1e35zJ8>PW1!ai z&EE$b+vo^NZ1ieotu&X*)R5W30xX?Q%fy`Bsr8i|1X~koB24KFc zOB7!pn_)$JZ>@J^1p6jcSWtxxa=KZ