gt:         return ((fragmenter == null) || fragmenter.isEmpty()) &&
context: /*
   * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package sun.security.ssl;
  
  import java.io.*;
  import java.nio.*;
  import java.util.*;
  
  import javax.crypto.BadPaddingException;
  
  import javax.net.ssl.*;
  
  import sun.security.util.HexDumpEncoder;
  import static sun.security.ssl.Ciphertext.RecordType;
  
  /**
   * DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
   */
  final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
  
      private DTLSFragmenter fragmenter = null;
  
      int                 writeEpoch;
  
      int                 prevWriteEpoch;
      Authenticator       prevWriteAuthenticator;
      CipherBox           prevWriteCipher;
  
      private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
  
      DTLSOutputRecord() {
          this.writeAuthenticator = new MAC(true);
  
          this.writeEpoch = 0;
          this.prevWriteEpoch = 0;
          this.prevWriteCipher = CipherBox.NULL;
          this.prevWriteAuthenticator = new MAC(true);
  
          this.packetSize = DTLSRecord.maxRecordSize;
          this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
      }
  
      @Override
      void changeWriteCiphers(Authenticator writeAuthenticator,
              CipherBox writeCipher) throws IOException {
  
          encodeChangeCipherSpec();
  
          prevWriteCipher.dispose();
  
          this.prevWriteAuthenticator = this.writeAuthenticator;
          this.prevWriteCipher = this.writeCipher;
          this.prevWriteEpoch = this.writeEpoch;
  
          this.writeAuthenticator = writeAuthenticator;
          this.writeCipher = writeCipher;
          this.writeEpoch++;
  
          this.isFirstAppOutputRecord = true;
  
          // set the epoch number
          this.writeAuthenticator.setEpochNumber(this.writeEpoch);
      }
  
      @Override
      void encodeAlert(byte level, byte description) throws IOException {
          RecordMemo memo = new RecordMemo();
  
          memo.contentType = Record.ct_alert;
          memo.majorVersion = protocolVersion.major;
          memo.minorVersion = protocolVersion.minor;
          memo.encodeEpoch = writeEpoch;
          memo.encodeCipher = writeCipher;
          memo.encodeAuthenticator = writeAuthenticator;
  
          memo.fragment = new byte[2];
          memo.fragment[0] = level;
          memo.fragment[1] = description;
  
          alertMemos.add(memo);
      }
  
      @Override
      void encodeChangeCipherSpec() throws IOException {
          if (fragmenter == null) {
             fragmenter = new DTLSFragmenter();
          }
          fragmenter.queueUpChangeCipherSpec();
      }
  
      @Override
      void encodeHandshake(byte[] source,
              int offset, int length) throws IOException {
  
          if (firstMessage) {
              firstMessage = false;
          }
  
          if (fragmenter == null) {
             fragmenter = new DTLSFragmenter();
          }
  
          fragmenter.queueUpHandshake(source, offset, length);
      }
  
      @Override
      Ciphertext encode(ByteBuffer[] sources, int offset, int length,
              ByteBuffer destination) throws IOException {
  
          if (writeAuthenticator.seqNumOverflow()) {
              if (debug != null && Debug.isOn("ssl")) {
                  System.out.println(Thread.currentThread().getName() +
                      ", sequence number extremely close to overflow " +
                      "(2^64-1 packets). Closing connection.");
              }
  
              throw new SSLHandshakeException("sequence number overflow");
          }
  
          // not apply to handshake message
          int macLen = 0;
          if (writeAuthenticator instanceof MAC) {
              macLen = ((MAC)writeAuthenticator).MAClen();
          }
  
          int fragLen;
          if (packetSize > 0) {
              fragLen = Math.min(maxRecordSize, packetSize);
              fragLen = writeCipher.calculateFragmentSize(
                      fragLen, macLen, headerSize);
  
              fragLen = Math.min(fragLen, Record.maxDataSize);
          } else {
              fragLen = Record.maxDataSize;
          }
  
          if (fragmentSize > 0) {
              fragLen = Math.min(fragLen, fragmentSize);
          }
  
          int dstPos = destination.position();
          int dstLim = destination.limit();
          int dstContent = dstPos + headerSize +
                                  writeCipher.getExplicitNonceSize();
          destination.position(dstContent);
  
          int remains = Math.min(fragLen, destination.remaining());
          fragLen = 0;
          int srcsLen = offset + length;
          for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
              int amount = Math.min(sources[i].remaining(), remains);
              int srcLimit = sources[i].limit();
              sources[i].limit(sources[i].position() + amount);
              destination.put(sources[i]);
              sources[i].limit(srcLimit);         // restore the limit
              remains -= amount;
              fragLen += amount;
          }
  
          destination.limit(destination.position());
          destination.position(dstContent);
  
          if ((debug != null) && Debug.isOn("record")) {
              System.out.println(Thread.currentThread().getName() +
                      ", WRITE: " + protocolVersion + " " +
                      Record.contentName(Record.ct_application_data) +
                      ", length = " + destination.remaining());
          }
  
          // Encrypt the fragment and wrap up a record.
          long recordSN = encrypt(writeAuthenticator, writeCipher,
                  Record.ct_application_data, destination,
                  dstPos, dstLim, headerSize,
                  protocolVersion, true);
  
          if ((debug != null) && Debug.isOn("packet")) {
              ByteBuffer temporary = destination.duplicate();
              temporary.limit(temporary.position());
              temporary.position(dstPos);
              Debug.printHex(
                      "[Raw write]: length = " + temporary.remaining(),
                      temporary);
          }
  
          // remain the limit unchanged
          destination.limit(dstLim);
  
          return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
      }
  
      @Override
      Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
          if (alertMemos != null && !alertMemos.isEmpty()) {
              RecordMemo memo = alertMemos.pop();
  
              int macLen = 0;
              if (memo.encodeAuthenticator instanceof MAC) {
                  macLen = ((MAC)memo.encodeAuthenticator).MAClen();
              }
  
              int dstPos = destination.position();
              int dstLim = destination.limit();
              int dstContent = dstPos + headerSize +
                                  writeCipher.getExplicitNonceSize();
              destination.position(dstContent);
  
              destination.put(memo.fragment);
  
              destination.limit(destination.position());
              destination.position(dstContent);
  
              if ((debug != null) && Debug.isOn("record")) {
                  System.out.println(Thread.currentThread().getName() +
                          ", WRITE: " + protocolVersion + " " +
                          Record.contentName(Record.ct_alert) +
                          ", length = " + destination.remaining());
              }
  
              // Encrypt the fragment and wrap up a record.
              long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
                      Record.ct_alert, destination, dstPos, dstLim, headerSize,
                      ProtocolVersion.valueOf(memo.majorVersion,
                              memo.minorVersion), true);
  
              if ((debug != null) && Debug.isOn("packet")) {
                  ByteBuffer temporary = destination.duplicate();
                  temporary.limit(temporary.position());
                  temporary.position(dstPos);
                  Debug.printHex(
                          "[Raw write]: length = " + temporary.remaining(),
                          temporary);
              }
  
              // remain the limit unchanged
              destination.limit(dstLim);
  
              return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
          }
  
          if (fragmenter != null) {
              return fragmenter.acquireCiphertext(destination);
          }
  
          return null;
      }
  
      @Override
      boolean isEmpty() {
  