lib/io/stream/shim/buffered.rb



# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2023-2025, by Samuel Williams.

unless IO.method_defined?(:buffered?, false)
	class IO
		# Check if the IO is buffered.
		# @returns [Boolean] True if the IO is buffered (not synchronized).
		def buffered?
			return !self.sync
		end
		
		# Set the buffered state of the IO.
		# @parameter value [Boolean] True to enable buffering, false to disable.
		def buffered=(value)
			self.sync = !value
		end
	end
end

require "socket"

unless BasicSocket.method_defined?(:buffered?, false)
	# Socket extensions for buffering support.
	class BasicSocket
		# Check if this socket uses TCP protocol.
		# @returns [Boolean] True if the socket is TCP over IPv4 or IPv6.
		def ip_protocol_tcp?
			local_address = self.local_address
			
			return (local_address.afamily == ::Socket::AF_INET || local_address.afamily == ::Socket::AF_INET6) && local_address.socktype == ::Socket::SOCK_STREAM
		end
		
		# Check if the socket is buffered.
		# @returns [Boolean] True if the socket is buffered.
		def buffered?
			return false unless super
			
			if ip_protocol_tcp?
				return !self.getsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY).bool
			else
				return true
			end
		end
		
		# Set the buffered state of the socket.
		# @parameter value [Boolean] True to enable buffering, false to disable.
		def buffered=(value)
			super
			
			if ip_protocol_tcp?
				# When buffered is set to true, TCP_NODELAY shold be disabled.
				self.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, value ? 0 : 1)
			end
		rescue ::Errno::EINVAL
			# On Darwin, sometimes occurs when the connection is not yet fully formed. Empirically, TCP_NODELAY is enabled despite this result.
		rescue ::Errno::EOPNOTSUPP
			# Some platforms may simply not support the operation.
		end
	end
end

require "stringio"

unless StringIO.method_defined?(:buffered?, false)
	# StringIO extensions for buffering support.
	class StringIO
		# Check if the StringIO is buffered.
		# @returns [Boolean] True if the StringIO is buffered (not synchronized).
		def buffered?
			return !self.sync
		end
		
		# Set the buffered state of the StringIO.
		# @parameter value [Boolean] True to enable buffering, false to disable.
		def buffered=(value)
			self.sync = !value
		end
	end
end